implicitly deleted move constructor (C's move) looks like non-declared move and compiler try to use the copy constructor (C's copy).
Why there is the difference between the implicitly deleted move (C's move) and explicitly deleted move (B's move)?
There are some explanations, are there any more details?
Why is the move constructor neither declared nor deleted with clang?
#dyp comment:
Because an implicitly deleted move constructor is not declared
and link to DR1402:
http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1402
A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3 [over.match]). [Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. βend note]
http://en.cppreference.com/w/cpp/language/move_constructor
(since C++14)The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue)
sample code:
#include <utility>
class A {
public:
A() = default;
A(const A&) = delete;
A(A&&) = delete;
};
class B : public A {
public:
B() = default;
B(const B&) = delete;
B(B&&) = delete; // explicitly deleted move
};
class C: public A {
public:
C() = default;
C(const C&) = default; // implicitly deleted copy because A's copy is deleted
C(C&&) = default; // implicitly deleted move because A's move is deleted
};
int main() {
A a1;
A a2(std::move(a1)); // error, A's move is explicitly deleted
B b1;
B b2(std::move(b1)); // error, B's move is explicitly deleted
C c1;
C c2(std::move(c1)); // error, C's **copy** is implicitly deleted
return 0;
}
compiler message:
Apple LLVM version 7.0.0 (clang-700.1.76)
clang++ -std=c++11 3.cpp
3.cpp:26:7: error: call to deleted constructor of 'A'
A a2(std::move(a1)); // error, A's move is explicited deleted
^ ~~~~~~~~~~~~~
3.cpp:7:5: note: 'A' has been explicitly marked deleted here
A(A&&) = delete;
^
3.cpp:29:7: error: call to deleted constructor of 'B'
B b2(std::move(b1)); // error, B's move is explicited deleted
^ ~~~~~~~~~~~~~
3.cpp:14:5: note: 'B' has been explicitly marked deleted here
B(B&&) = delete; // explicitly deleted move
^
3.cpp:32:7: error: call to implicitly-deleted copy constructor of 'C'
C c2(std::move(c1)); // error, C's **copy** is implicited deleted
^ ~~~~~~~~~~~~~
3.cpp:20:5: note: explicitly defaulted function was implicitly deleted here
C(const C&) = default; // implicitly deleted copy
^
3.cpp:17:10: note: copy constructor of 'C' is implicitly deleted because base class 'A' has a deleted copy constructor
class C: public A {
^
3.cpp:6:5: note: 'A' has been explicitly marked deleted here
A(const A&) = delete;
^
3 errors generated.
std::move does not necessarily force moving, it just casts the argument as an xvalue so that it may be moved, if possible. It can be ignored, and is most times unnecessary to call std::move to make the compiler move, or even better optimize copies away completely.
http://en.cppreference.com/w/cpp/utility/move
It is exactly equivalent to a static_cast to an rvalue reference type.
static_cast<typename std::remove_reference<T>::type&&>(t)
Related
The following code compiles in C++17 and does not compile in C++11
class Value
{
public:
Value() = default;
~Value() = default;
Value(const Value&) = default;
Value& operator=(const Value&) = default;
Value(Value&&) = delete;
Value& operator=(Value&&) = delete;
};
int main()
{
auto value = Value{};
}
The message is:
error: call to deleted constructor of 'Value'
auto value = Value{};
^ ~~~~~~~
note: 'Value' has been explicitly marked deleted here
Value(Value&&) = delete;
But if I change to:
int main()
{
Value value{};
}
Then it is fine for both C++11 and C++17.
Does this have anything to copy elision introduced in C++17?
"Copy elision" itself was not introduced in C++17, it was already present in prior versions of the standard. What allows your code snippet to be compiled in C++17 are the changes in section "11.6 Initializers" (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf):
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same
class as the class of the destination, the initializer expression is used to initialize the destination
object. [Example: T x = T(T(T())); calls the T default constructor to initialize x. β end
example ]
In your code snippet "Value{}" is a prvalue and it has the same type than the destination object, thus it is directly initialized, without having any copy or move operation involved. This is why that code can be compiled in C++17 but not in C++11.
Only for completeness, the introduced changes were part from this paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html
Item 17: Understand special member function generation.
Move operations are generated only for classed lacking
explicitly declared move operation, copy operations,
or a destructor.
Now, when I refer to a move operation move-constructing
or move-assigning a data member or base class, there
is no guarantee that a move will actually take place.
"Memberwise moves" are, in reality, more like memberwise
move requests, because types that aren't move-enabled(...)
will be "moved" via their copy operations.
However, I can not verify them on my environment.
// compiled
#include <iostream>
using namespace std;
class Base {
public:
~Base() {}
};
int main() {
Base a, c;
Base b(move(a));
c = move(b);
// explicitly destructor does not disable
// default move constuctor and move assignment operator
return 0;
}
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
int main() {
Num a, c;
c = move(a); // passed
Num b(move(c)); // error
// explicitly Base::Base(Base& b) disable default move
// move conctructor.
// Num's default move constructor can not find any move
// constructor for member object Base b, which lead to an
// error. Num's default move constructor does not "moved"
// Base b via their copy operations which is declared.
return 0;
}
The first assertion might be vary from different environments, but the second one is almost wrong.
I am very confusing about it.
Please help me out.
class Base {
public:
~Base() {}
};
Because Base has a user-declared destructor, it does not have a move constructor or move assignment operator at all. The lines
Base b(move(a));
c = move(b);
actually call the copy constructor and copy assignment operator, respectively.
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
Again, Base has no move constructor at all. However, Num does have an implicitly-declared move constructor, since Num itself does not declare any special member functions. However, it is implicitly defined as deleted, since the default definition would be ill-formed:
Num::Num(Num&& n) : b(std::move(n.b)) {}
// Cannot convert rvalue of type `Base` to `Base&`
// for the copy constructor to be called.
Note that the "move" of b does attempt to use the copy constructor, but it can't.
The following code will not compile on gcc 4.8.2.
The problem is that this code will attempt to copy construct an std::pair<int, A> which can't happen due to struct A missing copy and move constructors.
Is gcc failing here or am I missing something?
#include <map>
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A& operator=(const A&) = delete;
};
int main()
{
std::map<int, A> map;
map.emplace(1, 2); // doesn't work
map.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2)
); // works like a charm
return 0;
}
As far as I can tell, the issue isn't caused by map::emplace, but by pair's constructors:
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.
The issue is rooted in the fact that although we can construct
A t(4);
that is, std::is_constructible<A, int>::value == true, we cannot implicitly convert an int to an A [conv]/3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed,
for some invented temporary variable t.
Note the copy-initialization (the =). This creates a temporary A and initializes t from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A, which makes the conversion ill-formed.
As we cannot convert from an int to an A, the constructor of pair that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructor explicit instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.
The following describes the C++11 rules.
The constructor I had expected to be called is described in [pairs.pair]/7-9
template<class U, class V> constexpr pair(U&& x, V&& y);
7 Requires: is_constructible<first_type, U&&>::value is true and
is_constructible<second_type, V&&>::value is true.
8 Effects: The
constructor initializes first with std::forward<U>(x) and second with
std::forward<V>(y).
9 Remarks: If U is not implicitly convertible to
first_type or V is not implicitly convertible to second_type this
constructor shall not participate in overload resolution.
Note the difference between is_constructible in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).
Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a A const&. A temporary is created from the int argument and bound to this reference, and the reference is used to initialize the pair data member (.second). The initialization tries to call the deleted copy ctor of A, and the construction of the pair is ill-formed.
libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of pair that I had expected to be called (being surprised by the rules required by the Standard) is:
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
and the one that is actually called is the non-standard:
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.
As a final remark, here's the specification of is_constructible and is_convertible.
is_constructible [meta.rel]/4
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(create<Args>()...);
[Note: These tokens are never interpreted as a function declaration. β end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.
is_convertible [meta.unary.prop]/6:
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and
only if the return expression in the following code would be well-formed, including any implicit conversions
to the return type of the function:
To test() {
return create<From>();
}
[Note: This requirement gives well defined results for reference types, void types, array types, and function types. β end note] Access checking is performed as if in a context unrelated to To and From. Only
the validity of the immediate context of the expression of the return-statement (including conversions to
the return type) is considered.
For your type A,
A t(create<int>());
is well-formed; however
A test() {
return create<int>();
}
creates a temporary of type A and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&) and is therefore ill-formed.
class A { public: int x[100]; };
Declaring A a will not initialize the object (to be seen by garbage values in the field x).
The following will trigger initialization: A a{} or auto a = A() or auto a = A{}.
Should any particular one of the three be preferred?
Next, let us make it a member of another class:
class B { public: A a; };
The default constructor of B appears to take care of initialization of a.
However, if using a custom constructor, I have to take care of it.
The following two options work:
class B { public: A a; B() : a() { } };
or:
class B { public: A a{}; B() { } };
Should any particular one of the two be preferred?
Initialization
class A { public: int x[100]; };
Declaring A a will not initialize the object (to be seen by garbage
values in the field x).
Correct A a is defined without an initializer and does not fulfill any of the requirements for default initialization.
1) The following will trigger initialization:
A a{};
Yes;
a{} performs list initialization which
becomes value initialization if {} is empty, or could be aggregate initialization if A is an aggregate.
Works even if the default constructor is deleted. e.g. A() = delete; (If 'A' is still considered an aggregate)
Will warn of narrowing conversion.
2) The following will trigger initialization:
auto a = A();
Yes;
This is copy initialization where a prvalue temporary is constructed with direct initialization () which
uses value initialization if the () is empty.
No hope of aggregate initialization.
The prvalue temporary is then used to direct-initialize the object.
Copy elision may be, and normally is employed, to optimize out the copy and construct A in place.
Side effects of skipping copy/move constructors are allowed.
Move constructor may not be deleted. e.g A(A&&) = delete;
If copy constructor is deleted then move constructor must be present. e.g. A(const A&) = delete; A(A&&) = default;
Will not warn of narrowing conversion.
3) The following will trigger initialization:
auto a = A{}
Yes;
This is copy initialization where a prvalue temporary is constructed with list initialization {} which
uses value initialization if {} is empty, or could be aggregate initialization if A is an aggregate.
The prvalue temporary is then used to direct-initialize the object.
Copy elision may be, and normally is employed, to optimize out the copy and construct A in place.
Side effects of skipping copy/move constructors are allowed.
Move constructor may not be deleted. e.g A(A&&) = delete;
If copy constructor is deleted then move constructor must be present. e.g. A(const A&) = delete; A(A&&) = default;
Will warn of narrowing conversion.
Works even if the default constructor is deleted. e.g. A() = delete; (If 'A' is still considered an aggregate)
Should any particular one of the three be preferred?
Clearly you should prefer A a{}.
Member Initialization
Next, let us make it a member of another class:
class B { public: A a; };
The default constructor of B appears to take care of initialization
of a.
No this is not correct.
the implicitly-defined default constructor of 'B' will call the default constructor of A, but will not initialize the members. No direct or list initialization will be triggered. Statement B b; for this example will call the default constructor, but leaves indeterminate values of A's array.
1) However, if using a custom constructor, I have to take care of it. The
following two options work:
class B { public: A a; B() : a() { } };
This will work;
: a() is a constructor initializer and a() is a member initializer as part of the member initializer list.
Uses direct initialization () or, if () is empty, value initialization.
No hope of using aggregate initialization.
Will not warn of narrowing conversion.
2) or:
class B { public: A a{}; B() { } };
This will work;
a now has a non-static data member initializer, which may require a constructor to initialize it if you are using aggregate initialization and the compiler is not fully C++14 compliant.
The member initializer uses list initialization {} which
may become either value initialization if {} is empty or aggregate initialization if A is an aggregate.
If a is the only member then the default constructor does not have to be defined and the default constructor will be implicitly defined.
Clearly you should prefer the second option.
Personally, I prefer using braces everywhere, with some exceptions for auto and cases where a constructor could mistake it for std::initializer_list:
class B { public: A a{}; };
A std::vector constructor will behave differently for std::vector<int> v1(5,10) and std::vector<int> v1{5,10}. with (5,10) you get 5 elements with the value 10 in each one, but with {5,10} you get two elements containing 5 and 10 respectively because std::initializer_list is strongly preferred if you use braces. This is explained very nicely in item 7 of Effective Modern C++ by Scott Meyers.
Specifically for member initializer lists, two formats may be considered:
Direct initialization a() which becomes value initialization if the () is empty.
List initialization a{} which also becomes value initialization if {} is empty.
In member initializer lists, fortunately, there is no risk of the most vexing parse. Outside of the initializer list, as a statement on its own, A a() would have declared a function vs. A a{} which would have been clear. Also, list initialization has the benefit of preventing narrowing conversions.
So, in summary the answer to this question is that it depends on what you want to be sure of and that will determine the form you select. For empty initializers the rules are more forgiving.
I ran into a problem. I implemented a constructor for a class, but why are implicitly generated the other constructors, like the copy one?
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones. I'm really hoping, that this a VC++ specific thing, and that this code doesn't conforms to ISO:IEC C++11:
class Foo
{
int bar;
public:
Foo(int&& arg) : bar(arg) { cout << "RConstruction" << endl; }
};
int main(int, const char*[])
{
Foo f = Foo(42);
/* Create unused temporary on the stack */
Foo::Foo(f); /* calling Foo::Foo(const Foo&): this shouldn't work... */
return (0);
}
Please keep in mind, that this is a sample code, created exactly for this situation, for demonstration purposes, I expect answers only that strictly relate to this question.
That's not a move constructor, so it doesn't suppress any implicit ones.
Just like Foo(const int&) isn't a copy constructor, Foo(int&&) isn't a move constructor, it's just a constructor taking an rvalue reference.
A move constructor looks like one of:
Foo(Foo&&)
Foo(const Foo&&)
Foo(volatile Foo&&)
Foo(const volatile Foo&&)
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones.
If you define any constructor the compiler doesn't generate the default constructor, but it still generates the others. Define the as deleted if you don't want them:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
You did not declare a move constructor, but a regular constructor : no implicit constructor will be deleted.
A move constructor would be of the form Foo(Foo&& arg) (with any cv-qualifier on the argument)
Also note that this statement is not valid C++ :
Foo::Foo(f);
Maybe you meant :
Foo g = Foo(f);