awesoon
awesoon

Reputation: 33651

How could type, comprising non move constructible field, be move constructible?

This is the root of this question

I have different behavior while compiling the following code with clang++/libc++ and g++/libstdc++.

#include <type_traits>
#include <utility>
#include <iostream>

int main()
{
    using array_t = int[42];
    std::cout << "array_t:" << std::endl;
    std::cout << "    is_move_constructible: " << std::is_move_constructible<array_t>::value << std::endl;
    std::cout << "    is_move_assignable:    " << std::is_move_assignable<array_t>::value    << std::endl;
    std::cout << std::endl;

    using pair_t = std::pair<array_t, array_t>;
        std::cout << "pair_t:" << std::endl;
    std::cout << "    is_move_constructible: " << std::is_move_constructible<pair_t>::value << std::endl;
    std::cout << "    is_move_assignable:    " << std::is_move_assignable<pair_t>::value    << std::endl;
    std::cout << std::endl;

    pair_t p1;
    pair_t p2(std::move(p1));

    return 0;
}

clang(fails):

In file included from /home/soon/Src/C++/main/main.cpp:2:
/usr/include/c++/v1/utility:283:11: error: array initializer must be an initializer list
        : first(_VSTD::forward<first_type>(__p.first)),
          ^
/home/soon/Src/C++/main/main.cpp:20:12: note: in instantiation of member function 'std::__1::pair<int [42], int [42]>::pair' requested here
    pair_t p2(std::move(p1));
           ^
In file included from /home/soon/Src/C++/main/main.cpp:2:
/usr/include/c++/v1/utility:284:11: error: array initializer must be an initializer list
          second(_VSTD::forward<second_type>(__p.second))
          ^

g++ compiles without errors. The output is:

array_t:
    is_move_constructible: 0
    is_move_assignable:    0

pair_t:
    is_move_constructible: 1
    is_move_assignable:    1

I can't determine, which is correct. As I guess, if a class contains non-move-constructible field, it can't be constructed using move technique. Is that correct?

Upvotes: 0

Views: 226

Answers (3)

mfontanini
mfontanini

Reputation: 21900

Type traits can be kind of tricky. is_move_constructible/assignable check whether a type contains a move constructor/assignment operator(either explicitly or implicitly defined). That's it. Those type traits won't detect if the actual instantiation of those constructors/operators is ill formed.

Since std::pair contains a move constructor/assignment operator, then both is_move_constructible and is_move_assignable will be std::true_type. That only indicates that they're defined, but that doesn't mean that you can actually use them.

This has been discussed in this thread.

Upvotes: 1

Walter
Walter

Reputation: 45414

I should add to Sebastian's answer that clang-3.3 compiles your code w/o problems with the same result (at run-time) as gcc. So the misbehaviour of your version of the clang compiler appears to be a bug, that has been fixed by now.

Upvotes: 1

Sebastian Redl
Sebastian Redl

Reputation: 71899

I don't know whether GCC (or rather libstdc++) is right in allowing this code, but if it is, then the result is correct. The default move constructor will be generated iff all members either have their own move constructor, or are trivially copyable. Pair's move constructor is specified to be defaulted, so it follows these rules. Primitives and arrays of them are trivially copyable.

I suspect that libstdc++ is right, and for some reason you're entering the compiler compatibility branch in libc++ for _LIBCPP_HAS_NO_DEFAULTED_FUNCTIONS, which doesn't support arrays because it doesn't use = default. What version of Clang are you using, and are you correctly specifying -std=c++11?

Upvotes: 1

Related Questions