Why auto v {create()}; does not compile? - c++

i was doing some tests of the move semantics and I tried this :
class A{
public:
A(){printf("A CTOR\n");}
A(A&) {printf("A CTOR by copy\n");}
A(A&&){printf("A CTOR by universal reverence\n");}
};
A&& create(){
A v;
return std::move(v);
}
auto x{ create() };//this does not compile
float d[]{1.2,1.3,5,6};//this does compile
I get the following error:
error C3086: cannot find 'std::initializer_list': you need to #include <initializer_list>
I don't understand as the initializer list feature has been added to VC11 with CTP2012 nov.
Is this because we have to wait for an update of the stdlib ?
I think the code is correct as I copied it from a slide from Scott meyers: Move Semantics, Rvalue References, Perfect Forwarding.
Thank you for your help.
For your information, the spurious copies occured because i did not add "const" in my CTOR by copy.
Best

Braces with auto will always end up in std::initializer_list<T> type. So basically what happens here you try to create a std::initializer_list<A> here for x instead of A what probably was your intent here. However your code should compile fine and that may be a bug in the VS latest CTP compiler.
To make x be of A you have 2 options:
Do not use auto here:
A x{ create() };
Do not use Uniform initialization here:
auto x(create());
Beside that I see 2 other issues in you code. Firstly signature of proper copy-constructor should look like that:
A(const A &) {printf("A CTOR by copy\n");}
Moreover it is not encouraged to return RValue references from functions. Instead you should return by value like that:
A create(){
A v;
return v; // std::move not needed here because C++11 compiler will put it for you here
}
or just:
A create(){
return A();
}
EDIT:
Oh and just to be politically correct A(A&&) is not "A CTOR by universal reference". It is "A CTOR by move" or "A CTOR by rvalue reference" depending on which level of abstraction you want to operate. Universal reference is always about templates and that specific type type deduction.
Please also read an article of Scott Meyers I attached in a different comment http://scottmeyers.blogspot.com/2012/10/copying-constructors-in-c11.html where the same C++ guru that you refer in your notes exactly explains the problems you face. Hope that helps.

The line
auto x{ create() };//this does not compile
creates an initializer_list and not an instance of A, see for example std::initializer_list.
If you write
A x{ create() };
the code compiles and the type of x will be A.
I wouldn't return A&& from create(), see Move Semantics and Compiler Optimizations. I haven't seen this in Scott's slides either.
The following code prints only A CTOR, see here. That is the best you can get: The object has to be created. Apart from that there are no unnecessary copy constructor or assignment operator calls.
#include <cstdio>
#include <utility>
using namespace std;
class A{
public:
A(){ printf("A CTOR\n");}
A(const A&) {printf("A CTOR by copy\n");}
A(A&&){ printf("A CTOR by universal reverence\n");}
A& operator=(const A& ) = delete;
};
A create(){
return A();
}
int main() {
A x{ create() };
return 0;
}
std::move is not the only way to get rid of the unnecessary copies. The compiler can do it for you, see for example What are copy elision and return value optimization? or Copy elision.

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.

Copy ctor is called instead of move ctor - Can compiler give a warning?

In the following code I want to move-construct an object that has no move-constructor available:
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = delete;
};
SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
The compiler gives an error: "use of deleted function". This is all good.
On the other hand, if it has a move-constructor but getObject() returns a const object, then the copy constructor will be called instead, even though I'm trying to move it with std::move.
Is it possible to make the compiler give a warning / error that std::move won't have any effect since the object can't be moved?
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = default;
};
const SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
If your concern is just knowing for sure that you're getting the best possible performance, most of the time you should probably just trust the compiler. In fact, you should almost never need to use std::move() at all. In your example above, for example, it's not having an effect. Modern compilers can work out when a move should happen.
If you have classes that should always be moved and never copied, then delete their copy constructors.
But maybe you're writing a template function that is going to have terrible performance if you pass it a class without a move constructor, or you're in some other situation I haven't thought of. In that case, std::is_move_constructible is what you probably want. Try this:
#include <type_traits>
#include <boost/serialization/static_warning.hpp>
template<class T>
T &&move_or_warn(T &t)
{
BOOST_STATIC_WARNING(std::is_move_constructible<T>::value);
return std::move(t);
}
Now if you do SomeClass obj = std::move_or_warn( getObject());, you should get a compiler warning if the object can't be moved. (Although I'd probably use the normal std::move and call std::is_move_constructible seperately.)
Unfortunately, C++ doesn't (yet) have a standard way to produce the sort of programmer-specified warning you're looking for, which is why I had to use boost. Take a look here for more discussion about generating warnings.
The problem comes from const rvalue. Such arguments match const T& better than T&&. If you really want to ban such arguments, you can add an overloaded move constructor:
SomeClass(const SomeClass&&) = delete;
Note: What you are trying is to ban such arguments, rather to ban the move behavior. Because we are usually not able to "steal" resource from a const object even if it is an rvalue, it is reasonable to call copy constructor instead of move constructor. If this is an XY problem, you should consider if it is really intended to ban such arguments.
Is it possible to make the compiler give a warning / error that std::move won't have any effect since the object can't be moved?
It is not exactly true that std::move won't have any effect. The following code (try it on wandbox):
void foo(const SomeClass&) {
std::cout << "calling foo(const SomeClass&)" << std::endl;
}
void foo(const SomeClass&&) {
std::cout << "calling foo(const SomeClass&&)" << std::endl;
}
int main() {
foo(getObject());
foo(std::move(getObject()));
}
will output
calling foo(const SomeClass&)
calling foo(const SomeClass&&)
even though your object has a deleted move constructor.
The reason is that std::move doesn't by itself "move" anything. It just does a simple cast (C++17 N4659 draft, 23.2.5 Forward/move helpers):
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept;
Returns: static_cast<remove_reference_t<T>&&>(t)
This is why the compiled doesn't give a warning - everything is perfectly legal, the cast you are doing has nothing to do with the deleted move constructor, and overload resolution selects the copy constructor as the best match.
Of course, you can define your own move with different semantics than std::move if you really need such semantics (like the one in matthewscottgordon's answer).

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.

Why copy constructor is called instead of move constructor?

please look at the following example code:
#include <iostream>
struct Foo {
Foo() { std::cout << "Default!\n"; }
Foo(const Foo& foo) { std::cout << "Copy!\n"; }
Foo(Foo&& foo) { std::cout << "Move!\n"; }
};
struct Bar {
Foo foo;
Bar() {}
Bar(Bar &that) : foo(that.foo) {}
Bar(Bar &&that) : foo(std::move(that.foo)) {}
};
Bar f() {
Bar bar;
return bar;
}
int main() {
Bar bar(f());
}
I'am expecting that the output of this code should be:
Default!
Move!
but what I get is:
Default!
Copy!
I can't see any reason why the copy constructor is called instead of the move constructor. If I put the keyword const in front of Bar &that in the declaration of the copy constructor of struct Bar, I've got the right result. I know that it is better for many cases to take a const lvalue reference rather than just an lvalue reference for copy constructors, but I just want to know the reason why this happened.
Why in this example Bar & has been preferred over Bar && even though the return value of f() should be considered as a prvalue? Why the keyword const solves the problem? Does const really solve the problem? Does it have something to do with RVO (Return Value Optimization)? Or, is this just a compiler bug?
I've tested this example on Visual C++ November 2012 CTP.
I've found a similar issue here:
Copy constructor is being called instead of the move constructor
But I still can't understand why.
Can anyone help me?
It's just the usual non-compliance of Visual C++ allowing binding a non-const lvalue reference to a temporary. It violates the language rules, but it went too long before being caught, so now there's code that depends on the bug that would break if it were properly fixed.
That behavior mistakenly allowing the non-const copy constructor to be used, combined with incomplete rvalue reference support in that Visual C++ version, evidently results in the wrong overload being selected.
If you want to use C++11/C++14 features, you'd best stay on top of the latest Visual C++ version.
Wow, when I compile this with...
Visual Studio in Debug I see "Default! Copy!".
Visual Studio in Release I see "Default!".
If you change Bar(Bar &that) to Bar(const Bar &that) then "Default! Move!"
Shockingly, if you switch the order of Bar(Bar &that) with Bar(Bar &&that) (so that the move ctor is defined first) then you'll actually see "Default! Move!"
Your question is probably answered here.
A temporary object cannot bind to a non-const reference. The copy constructor must take a reference to a const object to be able to make copies of temporary objects.
The other thing is that temporary objects shall not be modifiable as they are to be destroyed anytime soon. Holding a reference to temporaries leads to potential modification on nonexistent objects out of carelessness.

Template functions: default construction without copy-constructing in C++

Considering
struct C {
C() { printf("C::C()\n" ); }
C(int) { printf("C::C(int)\n" ); }
C( const C& ) { printf("copy-constructed\n"); }
};
And a template function
template< typename T > void foo(){
// default-construct a temporary variable of type T
// this is what the question is about.
T t1; // will be uninitialized for e.g. int, float, ...
T t2 = T(); // will call default constructor, then copy constructor... :(
T t3(); // deception: this is a local function declaration :(
}
int main(){
foo<int>();
foo<C >();
}
Looking at t1, it will not be initialized when T is e.g. int. On the other hand, t2 will be copy-constructed from a default constructed temporary.
The question: is it possible in C++ to default-construct a generic variable, other than with template-fu?
Here's a trick you can use, using a local class:
template <typename T> void foo() {
struct THolder {
T obj;
THolder() : obj() { } // value-initialize obj
};
THolder t1; // t1.obj is value-initialized
}
I think I read about this trick from an answer to another Stack Overflow question, but I am unable to find that question at the moment.
Alternatively, you can use the boost::value_initialized<T> class template, which basically does the same thing, with greater flexibility and consistency and with workarounds for buggy compilers.
In C++0x, it's much easier: you can use an empty initializer list:
T obj{}; // obj is value-initialized
(To the best of my knowledge, only gcc 4.5+ supports C++0x initializer lists. Clang and Visual C++ don't yet support them.)
If you don’t care for the fact that the copy constructor must exist, and just want to prevent it being called:
Don’t worry: it won’t be. The copy constructor call will be elided in this situation. Always, and reliably – even when you compile with optimizations disabled (-O0).
What is your real question? The default constructor is called for t1 instance.
T t2 = T(); // will call default constructor, then copy constructor... :(
Not on my compiler (VC2008). Output for me is...
C::C()
C::C()
Which is what I'd expect it to do. Am I missing something?