Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move member function generation

Code:

#include <iostream>
#include <ios>
#include <string>
#include <type_traits>
#include <memory>

struct value
{
    ~value() = default;
    std::unique_ptr<std::string> s;
};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_move_constructible<value>::value << '\n';
    std::cout << std::is_move_assignable<value>::value    << '\n';

    using str_ptr = std::unique_ptr<std::string>;
    std::cout << std::is_move_constructible<str_ptr>::value << '\n';
    std::cout << std::is_move_assignable<str_ptr>::value    << '\n';

    return 0;
}

Output (compiled with g++ v4.7.2, http://ideone.com/CkW1tG):

false
false
true
true

As I expect, value is not move constructible and is not move assignable because:

~value() = default;

is a user-declared destructor, which prevents the implicit generation of move members according to section 12.8 (see below). If the destructor is removed then value is move constructible and move assignable, as I expect (http://ideone.com/VcR2eq).

However, when the definition of value is changed to (http://ideone.com/M8LHEA):

struct value
{
    ~value() = default;
    std::string s;      // std::unique_ptr<> removed
};

the output is:

true
true
true
true

value is unexpectedly move constructible and move assignable. Am I misunderstanding or is this a compiler bug?


Background: I provided an answer to this question and was informed that Tree<> was moveable, but I am unsure and am attempting to determine for certain if it is or not.


Section 8.4.2 Explicitly-defaulted functions of the c++11 standard (draft n3337):

Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (12.1 12.4, 12.8), which might mean defining them as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [ Note: Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base.—end note ]

Section 12.8 Copying and moving class objects (point 9):

If the definition of a class X does not explicitly declare a move constructor,
one will be implicitly declared as defaulted if and only if
- X does not have a user-declared copy constructor,
- X does not have a user-declared copy assignment operator,
- X does not have a user-declared move assignment operator,
- X does not have a user-declared destructor, and
- the move constructor would not be implicitly defined as deleted.
like image 721
hmjd Avatar asked Jun 03 '13 13:06

hmjd


People also ask

Are move constructor automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

What does a move assignment operator do?

Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state.

Which class methods are automatically generated by the compiler?

The special member functions are class (or struct) member functions that, in certain cases, the compiler automatically generates for you. These functions are the default constructor, the destructor, the copy constructor and copy assignment operator, and the move constructor and move assignment operator.

How many types of member function are there?

Explanation: There are basically 5 types of member functions possible. The types include simple, static, const, inline, and friend member functions.


1 Answers

std::is_move_constructible<T> is true iff std::is_constructible<T, T&&> is true, but that doesn't imply that such a construction will call a move constructor, only that it is possible to construct the type from an rvalue of the same type. Such a construction might use a copy constructor.

When value::s is a unique_ptr the type's copy constructor and copy assignment operator are defined as deleted, because the s member is not copyable. It does not have a move constructor and move assignment operator because, as you pointed out, it has a user-declared destructor. That means it has no copy constructor and no move constructor (and no other user-defined constructors that could accept an argument of type value&&) so std::is_constructible<value, value&&> is false.

When value::s is a string the type's copy constructor and copy assignment operator are not defined as deleted, because the s member is copyable, and so value is also copyable, and a CopyConstructible type is also MoveConstructible, because it's valid in this context:

value v1;
value v2 = std::move(v1);  // calls copy constructor

That means std::is_constructible<value, value&&> is true, even though it invokes the copy constructor not a move constructor.

like image 59
Jonathan Wakely Avatar answered Oct 05 '22 13:10

Jonathan Wakely