C++ auto with member initializer syntax and deleted copy constructor - c++

class A
{
int a;
public:
A(const A&) = delete;
A& operator=(const A&) = delete;
A() : a {0}
{ }
};
int main()
{
auto a = A {};
}
The above code does not compiles and i get the following error: C2280 'A::A(const A &)': attempting to reference a deleted function
I am using visual studio 2015 compiler. My understanding is with member initialization syntax compiler should directly use the default constructor which is what happens when there is no auto and in main i use A a{}. So i was wondering what is the deal with auto in this case.

auto a = A {};
is only valid if A is copy constructible or move constructible. The fact that you use auto is irrelevant. The same would be true for
A a = A {};
as well.
Declaring a copy constructor – even a deleted one – inhibits implicit declaration of a move constructor so your type A is neither copy constructible nor move constructible. If you add the line
A(A&&) = default;
to the body of A, the code should compile again.
In practice, the compiler will not actually call any copy or move constructor in this case and simply construct the object right in a. But the language rules demand that it must still reject the code which makes sense because whether a piece of code is allowed or not should not depend on an optional compiler optimization.
This behavior will (most likely) change in C++17.

Your understanding is correct, so let's see what's happening here, one step at a time.
A {};
As you said, member initialization syntax, completely kosher here.
auto a = (expression of some kind)
And then you're constructing auto a. After performing type inference, this becomes equivalent to...
A a = (expression of some kind)
Which looks like a copy constructor. Which you deleted.

you should use auto this way:
auto a = new A();
if you don't want to use auto this is c++11 way:
A a{};

Related

C++14 Function returns array of objects although copy constructor is deleted

from the old C++98 i am aware, that return types a copied (by value) if not mentioned in the function declaration/definition otherwise with the address operator '&'.
Now i am playing around with the concepts of auto and decltype to let the compiler determin the return type. In an example i worte a class A where with exception of the default ctor any other ctors are deleted (class A is taken from a real project - and i investigate some issues). An object of the class A is contructed together with an etl::array (Embedded template library, Array created on the stack with fixed size), see example code below.
#include <etl/array.h>
#include <iostream>
class A {
public:
A(std::uint8_t val) : _val(val){}
A(A const&) = delete; // copy delete
A& operator=(A&) = delete; // copy assign delete
A(A&&) = default; // move default
A& operator=(A&&) = delete;// move assign delete
~A() = default;
void whoAmI(){std::cout << " I am A! " << std::endl;}
private:
std::uint8_t _val;
};
decltype(auto) X(std::uint8_t val) {
return etl::array<A,1>{A{val}};
}
int main()
{
decltype(auto) x = X(5U);
for (auto& it : x) {
it.whoAmI();
}
}
I would expect, that the etl::array will be copied in the main() routine and assigned to the local variable x. I would not expect to have a copy of A in the array, due to the deleted copy ctor. However, the code compiles and i am able to call the function on the element of the etl::array. I cannot understand why this is working and why it is compiling at all? And I wonder what type decltype(auto) finally is. I have chosen decltype(auto) because of Scott-Meyers Item 2 and Item 3. To item 3, i am not sure to have a complete understanding on the decltype topic..
When I step through the code it works fine, leaving me pussled behind..
Any help on this topic is highly appreciated!
Thank you very much for your help, it is enlightening to me.
Now i finally know why it's working :-D - you made my day!
decltype(auto) is used to determine the type and the value category of an expression. When used as a return type, it lets the function decide what the returned value type should be, based on the expression in the return statement. In this example, since you are using a temporary in the returned expression, it will deduce to an rvalue.
In this declaration:
decltype(auto) x = X(5U);
the syntax is copy-initialization, which has the effect of initializing the variable x from the expression X(5U). You have a defaulted move-constructor, and the compiler uses this constructor to initialize x from the rvalue returned from X.
Note that from C++17, due to mandatory copy-elision, you could even delete the move constructor, and the code is still valid, since there is no constructor needed to initialize x.

C++ Object Array Initialization without Copy Constructor [duplicate]

I have an uncopiable class. Copying this would be problematic. I want to guarantee that it won't be ever copied, so I made its copy constructor deleted:
class A {
public:
A();
A(const A&) = delete;
};
A fun() {
return A();
};
int main() {
A a = fun();
};
Unfortunately, g++ won't compile this on the reason:
t.cc: In function ‘A fun()’:
t.cc:8:12: error: use of deleted function ‘A::A(const A&)’
return A();
^
t.cc:4:5: note: declared here
A(const A&) = delete;
^
t.cc: In function ‘int main()’:
t.cc:12:13: error: use of deleted function ‘A::A(const A&)’
A a = fun();
^
t.cc:4:5: note: declared here
A(const A&) = delete;
^
But this is a very clear situation where copy elision should be used, so the copy constructor shouldn't be ever called. Why is it so?
Until C++17 copy elision is an optimization the compiler is not required to do, so classes must be copyable since the compiler might want to copy (even if it actually does not). In C++17 copy elision will be guaranteed in many cases and then classes won't need copy ctors.
See also:
http://en.cppreference.com/w/cpp/language/copy_elision
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/
(the bit about "Guaranteed copy elision")
You could perhaps use the old trick of declaring the copy constructor in your class but not actually implement it? That should please the compiler as long as it does not actually invoke the copy ctor. I didn't test that, but I believe it should work for your case until C++17 arrives.
You can't force copy elision (yet) (see other answers).
However, you can provide a default move constructor for your class, this will move (and thus, not copy) the return value if RVO/NRVO is not possible. To do this you should add = default for your move constructors:
class A {
public:
A() = default;
A(const A&) = delete;
A(A&&) = default;
A& operator=(A&&) = default;
};
Example
Return value optimization (RVO and NRVO) does not mean the requirement that the types involved by copyable or movable is dropped. This requirement applies, whether you get RVO or not.
The most likely reason this is so is that copy elision is not (currently) enforced. It is an optimization that may take place, and it would not make sense for code to compile or not based on whether that optimization is applied in a particular implementation.
In C++17, RVO wil be enforced in some circumstances, and the requirements of copyability and movability will be dropped.

Copying parameter invokes deleted constructor when that constructor shouldn't be called

#include <memory>
template <typename T>
class Wrapper {
public:
Wrapper() = delete;
Wrapper(const Wrapper&) = delete;
Wrapper(Wrapper&&) = delete;
~Wrapper() = default;
Wrapper(const T&) = delete;
Wrapper(T&& in) : instance{std::move(in)} {}
T instance;
};
void foo(Wrapper<std::shared_ptr<int>>) {}
int main() {
auto ptr = std::make_shared<int>(1);
foo(std::move(ptr));
}
This has been working in C++17 so I never gave it thought but why does this code try and invoke the move constructor in C++14? Shouldn't it be constructed in place in the function argument? This seems to not be a problem with c++17 but is not compiling with c++14.
The only workaround I see is to make the foo parameter an rvalue, but is there anything I can do to make this work without making the parameter in foo an rvalue in C++14?
My first thought would be that a temporary would have to be constructor in order to be passed to the function but what is even more surprising is that even with -fno-elide-constructors and undeleting the move constructors and copy constructors those do not seem to be called! Is this a bug in gcc and clang both?
See https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 for the error
And see for the strange behavior https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz
When you call foo(std::move(ptr)); you are not giving it a Wrapper<std::shared_ptr<int>>. So, the compiler generates a temporary and uses that to construct foo's parameter. Now, this can be elided out and we can directly construct a Wrapper<std::shared_ptr<int>> but the move/copy constructor still needs to be accessible, even if it is never called.
With C++17 this no longer happens. We have guaranteed copy elision which means no temporary is ever materialized and instead the parameter is directly constructed.

just add destructor that do nothing can cause compile error (around std::move), why?

While I was learning std::move, I found a strange issue.
If I add only a destructor that do nothing to a perfect program, I will get a compile error.
#include <iostream>
using namespace std;
class M {
public:
int database = 0;
M &operator=(M &&other) {
this->database = other.database;
other.database = 0;
return *this;
}
M(M &&other) { *this = std::move(other); }
M(M &m) = default;
M() = default;
~M() { /* free db */ }
};
class B {
public:
M shouldMove;
//~B(){} //<--- ## Adding this line will cause compile error. ##
};
int main() {
B b;
B b2 = std::move(b); //## error at this line if the above line is added
return 0;
}
Live code: https://ideone.com/UTR9ob
The error is invalid initialization of non-const reference of type 'B&' from an rvalue of type 'std::remove_reference<B&>::type {aka B}'.
Question:
(1) Which rules of C++ syntax enforce that? In other words, what does the error mean?
(2) If I want to add destructor that do almost nothing (e.g. only print debug log) to B, do I really have to follow the rule-of-five instead? If no, how to make it compile? Following the rule of five just because of that is too tedious and dirty in my opinion.
I think the rule of zero is just a good practice.
However, from this example, it seems to me that it is a hard rule that if violated, I will get compile error.
The implicitly-declared move constructor is only present if a class does not have a user-declared destructor. Therefore the answer to 2. is YES.
The answer to 1. is that this is hard rule and can be found in 12.8, paragraph 9 of the standard:
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.
[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that
otherwise would have invoked the move constructor may instead invoke a copy constructor. — end note ]
The best way of getting this to run,is by using something like a smart pointer, i.e., a base class or member that does define all five special members (and very little else) so that you don't have to. In this case, an integer handle equivalent to std::unique_pointer should work well. However, keep in mind that databases, like files, can have errors while closing, so standard non-throwing destructor semantics don't cover all cases.

move ctor of class with a constant data member or a reference member

I have some problems understanding when and if the move constructor or move assignment operator are invoked, in particular in the context of a class with constant data member.
Consider the class
template<typename T> class A {
const*T const P ; // constant data member
explicit A(const*T p) : P(p) { std::cerr<<" ctor: P="<<P<<'\n'; }
void test() const { std::cerr" test: P="<<P<<'\n'; }
// move and copy constructors and assignment operators here
};
and the test program
class B {
int X[100];
A<B> get_a() const { return A<B>(this); }
};
int main() {
B b;
A<B> a = b.get_a(); // which operator/ctor is used for '=' here?
a.test();
}
then the results for compilation are different depending on the definitions provided for the move constructor and move assignment operator in class A<>, but also on compiler.
1 without any further declaration in class A<> (as above), both g++ (4.7.0) and icpc (13.0.1) compile fine (with option -std=c++11) and produce the expected output
ctor: P=0x7fffffffd480
test: P=0x7fffffffd480
2 if I declare
A&A::operator=(A&&) = delete;
A&A::operator=(const A&) = delete;
(which seems sensible in view of the constant data member which must be initialiser-list initialised), but don't provide any further ctor, compilation fails with g++ but is okay wich icpc. If in addition I define either (or both) of
A::A(A&&) = default;
A::A(const A&) = default;
both compilers are happy. However, g++ is not happy with the combination
A::A(A&&) = delete;
A::A(const A&) = default;
while icpc is happy.
3 If I play the same game as in 2, except that A::A(A&&) = default; is replaced by
A::A(A&&a) : P(a.P) { std::cerr<<" move ctor: P="<<P<<'\n'; } // never called?
(and equivalent for A::A(const A&)), the results are completely identical, in particular no output is generated from these explicit move and copy ctors.
So which operator is used for = in main()? (and why is no output produced in the last test?)
And why is this operation allowed here at all, given that A<> has a constant data member (the results are identical if I replace the member const*T const P; with const T&R)?
Finally, in case of the different behaviours of g++ and icpc, which, if any, is correct?
A<B> a = b.get_a();
is not an assignment, but an initialization of a from an rvalue. This syntax should fail under C++0x if
the move constructor is deleted,
the move constructor is declared explicit,
the copy constructor is deleted and no move constructor is defined,
no move constructor is defined and, at the same time the move-assignment is defined or deleted.
Declaration or deletion of the copy assignment operator should not have any influence.
Correction: Different to the copy constructor (which is synthesized even if a user-defined copy assignment operator is provided), the compiler does not synthesize a move constructor if a user-defined move assignment is defined. Hence, the above list should be amended by 4 (which I have done now).
Hence, in my opinion,
in [1] in the question, both compilers behave correctly,
in [2]/1, gcc behaves correctly (move assignment prevents generation of move constructor), icpc is wrong,
in [2]/2, both compilers are correct,
in [2]/3, gcc is correct, since the move constructor is explicitly deleted; icpc is wrong,
[3] is a mystery to me. Are you sure you're correct?