Search code examples
c++language-lawyerc++20list-initialization

Nested list-initialization with non-copyable type does not work


void foo(std::pair<int, std::vector<std::unique_ptr<int>>>&&) {}

foo({{}, {}});

The above does not compile with an error message reporting that it attempts to call into the copy constructor of std::vector<std::unique_ptr<int>> (which is deleted). However, the following works

void foo(std::vector<std::unique_ptr<int>>&&) {}
void foo(const std::vector<std::unique_ptr<int>>&) {}

foo({});

What rules am I missing that make the second example work and the first example not work?


Example: https://wandbox.org/permlink/wE913kQTulV94uDf


Solution

  • It seems I have an answer to my own question -- this does not work because the nested list initialization overload only has the following std::pair constructor to match

    pair( const T1& x, const T2& y );
    

    The other std::pair constructor with two arguments is templated without a default type, so it does not get considered

    template< class U1, class U2 >
    pair( U1&& x, U2&& y );
    

    If we were to change this to a type where the second constructor has default types or has non-templated rvalue overloads, then the above works

    template <typename T1, typename T2>
    class Pair {
    public:
        Pair(const T1&, const T2&) {}
        Pair(T1&&, T2&&) {}
    };
    
    void foo(Pair<int, std::vector<std::unique_ptr<int>>>&&) {}
    
    foo({{}, {}});
    

    https://wandbox.org/permlink/pbmme350X7XjM8cI

    Or

    template <typename T1, typename T2>
    class Pair {
    public:
        Pair(const T1&, const T2&) {}
        
        template <typename U1 = T1, typename U2 = T2>
        Pair(U1&&, U2&&) {}
    };
    
    void foo(Pair<int, std::vector<std::unique_ptr<int>>>&&) {}
    
    foo({{}, {}});
    

    https://wandbox.org/permlink/EuYSJ2n2WTg368YS