Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are all CopyConstructible types MoveConstructible types?

According to the working draft N3337 (the most similar draft to the published ISOC++11 standard) and cppreference.com, the answer is yes.

N3337:

Table 21 — CopyConstructible requirements (in addition to MoveConstructible) [copyconstructible] [...]

cppreference.com:

The type T satisfies CopyConstructible if

  • The type T satisfies MoveConstructible, and [...]

But according to the result of compiling main.cpp with gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4 and running a.out with the quoted statements in Ubuntu 14.04.3 LTS, the answer is not.

main.cpp:

#include <iostream>
#include <type_traits>

struct As
{
    As()=default;
    As(As&&)=delete;
    As(const As&)=default;
    As& operator=(As&&)=delete;
    As& operator=(const As&)=delete;
    ~As()=default;
};

int main()
{
    std::cout<<std::is_move_constructible<As>::value<<std::endl;
    std::cout<<std::is_copy_constructible<As>::value<<std::endl;

    return 0;
}

compiling and running from terminal:

$ g++ -std=c++11 main.cpp
$ ./a.out

the result (output):

0
1

Did I misunderstand something or is N3337 and cppreference.com wrong, or does gcc contains a bug?

like image 362
Dániel Sándor Avatar asked Jan 27 '16 15:01

Dániel Sándor


3 Answers

std::is_copy_constructible<T> is defined to be exactly std::is_constructible<T, const T&> i.e. it only tests that construction from a const lvalue is possible, it doesn't test all the properties of the CopyConstructible concept.

So your test does not show what you think it shows. Your type is not a CopyConstructible type, because it fails some of the other requirements.

As for the original question, yes. Because all CopyConstructible types must meet the requirements for MoveConstructible they are all MoveConstructible types. MoveConstructible doesn't require that anything gets moved, only that construction from rvalues is possible, and all CopyConstructible types can be constructed from rvalues (even if they might do a deep copy not a move).

You can create perverse types that can be copied from lvalues but not from rvalues, or can be copied from const lvalues but not non-const lvalues, and other abominations. Such types are not CopyConstructible, and do not work well with the C++ standard library. There are very few good reasons to ever create perverse types like that.

like image 121
Jonathan Wakely Avatar answered Nov 14 '22 17:11

Jonathan Wakely


Your example misleads you somewhat.

As(As&&)=delete;

By deleting the move constructor, you make it illegal to construct an As with a As&&, even though the copy constructor could be called instead due to it taking a reference-to-const.

An example which displays the behaviour you are looking for is this:

struct As
{
    As()=default;
    As(const As&)=default;
    As& operator=(As&&)=delete;
    As& operator=(const As&)=delete;
    ~As()=default;
};

I just removed the deletion of the move constructor. As will not have a move constructor implicitly declared because it has a bunch of other user-declared special functions*. If you run your tests on this example, you'll see that the class is move constructible even though it has no move constructor.

Live Demo


*Specificially, the move constructor will not be implicitly declared if there's a user-declared copy constructor, copy assignment operator, move assignment operator or destructor.

like image 25
TartanLlama Avatar answered Nov 14 '22 17:11

TartanLlama


is_copy_constructible doesn't require the the type to be moveable. When it says

CopyConstructible requirements (in addition to MoveConstructible) [copyconstructible]

That means that to be CopyConstructible the class has to satisfy the requirements of MoveConstructible which are

T u = rv;       u is equivalent to the value of rv before the construction
T(rv)           T(rv) is equivalent to the value of rv before the construction

rv’s state is unspecified. [ Note: rv must still meet the requirements of the
library component that is using it. The operations listed in those requirements
must work as specified whether rv has been moved from or not. — end note ]

In addition to [copyconstructible]

T u = v;        the value of v is unchanged and is equivalent to u
T(v)            the value of v is unchanged and is equivalent to T(v)

The reason std::is_move_constructible<As>::value is false is that you have a deleted move constrcutor which prohibits move construction. Having a non deleted move constructor and satisfying [moveconstructible] are required for it to be true.

like image 2
NathanOliver Avatar answered Nov 14 '22 18:11

NathanOliver