Under the C++11 standard, is the following pair guaranteed to be moved into the function?
//objects available: key, value
//corresponding type available: pairtype
//function available: void foo(pairtype pair); copies the pair by default
foo({std::move(key),std::move(value)}); //pair moved?
or do I have to do the move myself?
foo(std::move(pairtype(std::move(key),std::move(value))); //needed?
Initializer lists are not expressions, so they do not have a type and they do not yield a value. This means that the following:
{std::move(key),std::move(value)}
Does not in itself create a pair. Initializer lists are just a syntactic construct used for initialization, and in this case the function parameter will be constructed by directly invoking the constructor of pairtype with std::move(key) and std::move(value) as arguments.
There is no creation of temporaries involved - the only thing to be aware of is that explicit constructors will not be considered when performing list-initialization (for instance, this would not work with an instance of std::tuple<>).
Which means that the invocation of foo we just discussed, i.e.:
foo({std::move(key),std::move(value)}
Is technically different from this invocation:
foo(std::move(pairtype(std::move(key),std::move(value)))
Here, you are intentionally creating a temporary and moving it into the function parameter (the compiler may then elide this move per 12.8/31, but this is another story).
Notice, that the call to std::move() here is superfluous, since temporaries are rvalues. The function parameter will be move-constructed from the temporary object anyway. You can therefore write:
foo(pairtype(std::move(key),std::move(value)))
Notice, that pairtype will be an instance of the std::pair<> class template, which means you will have to specify template arguments manually. To avoid this, you can use std::make_pair():
foo(std::make_pair(std::move(key),std::move(value)))
Related
The following compiles on several different compilers including g++, clang, and MSVC, but I cannot figure out why:
#include <string>
class C {
std::string m;
};
void Accept(C &&);
extern C c;
int main() {
Accept({{c}});
}
The generated assembly code appears to show a call to the copy constructor of std::string before then calling Accept(), which I am assuming means that the compiler generated a call to class C's copy constructor. It therefore appears that using {{c}} as an argument to an rvalue reference parameter creates a temporary copy.
Is this an accurate interpretation? And if so, what C++ language feature or features are combining to allow {{c}} to create a temporary copy of c?
How objects and references are initialized is specified in [dcl.init]. Specifically [dcl.init]/16.1 says that initialization from braces instead of an expression is list-initialization.
How list-initialization is performed is specified in [dcl.init.list]/3. The first item in the chain that applies if the destination type is a reference is [dcl.init.list]/3.9, which says that if the braced-init-list from which we initialize has exactly one element of a type that is reference-related to the destination type, then the reference is initialized from that element instead. So Accept({c}); would initialize the reference in the function parameter by copy-initialization from c, causing it to bind directly to c.
You seem to expect this behavior to apply recursively if there are multiple layers of braces as well. However, in {{c}} the braced-init-list has exactly one element which itself is a braced-init-list {c} and braced-init-lists do not have a type (they are not expressions), so item [dcl.init.list]/3.9 on the list can't apply. The following item [dcl.init.list]/3.10 then applies to reference destination types and without further conditions specifies that a prvalue of the referenced type will be created and used to initialize the reference, meaning that a temporary object will be materialized that the function parameter will be bound to.
So nested braces will always initialize a reference with a temporary object, no matter what.
Specifically in your case it will end up as a temporary object initialized by a call to the implicitly-declared copy constructor of C which initializes the reference in the copy constructor's parameter with {c}, which in turn as described above means it binds directly to c, so that no further temporary will be created. It does not end up with aggregate initialization instead, because your class member is private and so the class is not an aggregate. If it was an aggregate (e.g. because you replace class with struct), then the initialization of the temporary would be ill-formed, because std::string can't be initialized from a C.
However, it seems that (at least in 2017) the C++ standard committee had some intentions to make your code ill-formed, see the note in CWG issue 2319. The suggestion at the end of the issue description would also make the initialization of the temporary object in your call Accept({{c}}) ill-formed for the non-aggregate case by disallowing the copy/move constructor to be used with nested braces.
suppose we are implementing a vector class and we do
vector v;
v={1,2,3,4};
so,here we are implementing our own vector class so,how this list pass to assignment operator as an argument how we handle it because we passing constant which are not accessible by pointer variable and reference variable.
But all this can be done in stl vector class, so how did they do?
Prior to C++17, all arguments to all functions name fully constructed objects.
A prvalue std::initialiser_list<int> object (with no name) is constructed from {1,2,3,4}, and that is the constructor's argument. That temporary object ceases to exist once v's constructor ends (specifically at the end of the full expression that creates it).
From C++17 onward, a value that will initialise an object can be passed around. We still call it a prvalue std::initialiser_list<int>, but it isn't an object (yet). Somewhere in v's initialisation, where it gets used, a std::initialiser_list<int> object is constructed. It also ceases to exist after the constructor is done.
I'm putting a class in my app under std::shared_ptr, converting existing code. My understanding is that shared_ptr's constructor from a raw pointer is explicit (e.g., http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/), and that makes a great deal of sense to me. However, I'm seeing a weird thing in my app where the constructor seems to me to be acting as if it is implicit. I don't understand how this possible.
First of all, I have a typedef for my shared_ptr type:
typedef std::shared_ptr<EidosPropertySignature const> EidosPropertySignature_CSP;
Then I have some code where I construct a new object and add it to a vector of shared pointers:
std::vector<EidosPropertySignature_CSP> properties;
properties.emplace_back(new EidosPropertySignature(...constructor parameters...));
This compiles without even a warning. I don't see how – the result of new EidosPropertySignature is an EidosPropertySignature *, which should not be implicitly convertible to the EidosPropertySignature_CSP that the vector holds, right? I would very much like this construct to produce a compile error, as I thought that it would. Why doesn't it, and is there a way for me to modify my approach so that it does?
There is no implicit conversion here. emplace_back forwards its arguments directly to the constructor of the element type to construct the new element [sequence.reqmts]:
Appends an object of type T constructed with std::forward<Args>(args)....
Thus, emplace_back is essentially a direct constructor call. push_back, on the other hand, won't work because that would actually require a conversion…
On a purely conceptional level, implicit conversions are about allowing a value of some type to be converted to a value of a different type if necessary, possibly as part of a longer conversion sequence. Conversions are about building bridges. With an implicit conversion, you tell the compiler: here's a bridge from A to B, you may use it to get around. With an explicit conversion, you say: here's a bridge from A to B, use it only if I explicitly told you to go to B; in particular, don't use it if you just want to make your way to somewhere else over B. When you write emplace_back, you explicitly say "construct an element". emplace_back explicitly expresses that an object of the element type be created. You explicitly told the compiler to go to B. There is nothing implicit about emplace_back…
Because with the usage of emplace_back, it is not implicit.
That function will look something like template<class... Args> reference emplace_back(Args&&... args), so in this case you have one argument, EidosPropertySignature *, so no problem there.
The std::shared_ptr constructor will then be called with std::forward<Args>(args)..., which can call the explicit constructor.
template<class T, class... Args> T construct(Args...args)
{
return T(std::forward<Args>(args)...);
}
int main()
{
int *x = new int();
std::shared_ptr<int> a = x; // error
std::shared_ptr<int> b(std::forward<int*>(x)); // OK
std::shared_ptr<int> c = construct<std::shared_ptr<int>>(x); // OK
}
If you look at std::vector::emplace_back:
The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container. The arguments args... are forwarded to the constructor as std::forward<Args>(args)....
What happens under the hood is essentially:
new (storage) EidosPropertySignature_CSP(<emplace_back args>)
Which invokes the explicit constructor of EidosPropertySignature_CSP.
emplace_back constructs the element in-place directly from the arguments being passed.
The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Note that std::allocator_traits<Alloc>::construct constructs the element as ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...) at last, which is direct initialization; the constructor of the element type is explicit or not doesn't matter.
(emphasis mine)
Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.
On the other hand, push_back takes shared_ptr as parameter type, when a raw pointer being passed it has to be converted to shared_ptr implicitly, which is ill-formed because explicit converting constructor won't be considered in copy initialization.
I am quite puzzled by the std::move stuff. Assume I have this
piece of code:
string foo() {
string t = "xxxx";
return t;
}
string s = foo();
How many times the string constructor is called? Is it 2 or 3?
Is the compiler going to use move for this line?
string s = foo();
If so, in the function I am not even returning rvalue reference, so how could the
compiler invoke the move constructor?
It depends on the compiler. In this case, the standard requires that there will be at least one constructor call. Namely, the construction of t.
But the standard allows the possibility of two others: the move-construction of the value output of foo from t, and the move-construction of s from the value output of foo. Most decent compilers will forgo these constructors by constructing t directly in the memory for s. This optimization is made possible because the standard allows these constructors to not be called if the compiler chooses not to.
This is called copy/move "elision".
If so, in the function I am not even returning rvalue reference, so how could the compiler invoke the move constructor?
You seem to be laboring under the misconception that && means "move", and that if there's no && somewhere, then movement can't happen. Or that move construction requires move, which also is not true.
C++ is specified in such a way that certain kinds of expressions in certain places are considered valid to move from. This means that the value or reference will attempt to bind to a && parameter before binding to a & parameter. Temporaries, for example, will preferentially bind to a && parameter before a const& one. That's why temporaries used to construct values of that type will be moved from.
If you have a function which returns a value of some type T, and a return expression is of the form return x, where x is a named variable of type T of automatic storage duration (ie: a function parameter or stack variable), then the standard requires that this return expression move construct the returned value from x.
The return value of foo is a temporary. The rules of C++ require that temporaries bind to && parameters before const&. So you get move construction into s.
A class must have a valid copy or move constructor for any of this syntax to be legal:
C x = factory();
C y( factory() );
C z{ factory() };
In C++03 it was fairly common to rely on copy elision to prevent the compiler from touching the copy constructor. Every class has a valid copy constructor signature regardless of whether a definition exists.
In C++11 a non-copyable type should define C( C const & ) = delete;, rendering any reference to the function invalid regardless of use (same for non-moveable). (C++11 §8.4.3/2). GCC, for one, will complain when trying to return such an object by value. Copy elision ceases to help.
Fortunately, we also have new syntax to express intent instead of relying on a loophole. The factory function can return a braced-init-list to construct the result temporary in-place:
C factory() {
return { arg1, 2, "arg3" }; // calls C::C( whatever ), no copy
}
Edit: If there's any doubt, this return statement is parsed as follows:
6.6.3/2: "A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list."
8.5.4/1: "list-initialization in a copy-initialization context is called copy-list-initialization." ¶3: "if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7)."
Do not be misled by the name copy-list-initialization. 8.5:
13: The form of initialization (using parentheses or =) is generally insignificant, but does matter when the
initializer or the entity being initialized has a class type; see below. If the entity being initialized does not
have class type, the expression-list in a parenthesized initializer shall be a single expression.
14: The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.
Both copy-initialization and its alternative, direct-initialization, always defer to list-initialization when the initializer is a braced-init-list. There is no semantic effect in adding the =, which is one reason list-initialization is informally called uniform initialization.
There are differences: direct-initialization may invoke an explicit constructor, unlike copy-initialization. Copy-initialization initializes a temporary and copies it to initialize the object, when converting.
The specification of copy-list-initialization for return { list } statements merely specifies the exact equivalent syntax to be temp T = { list };, where = denotes copy-initialization. It does not immediately imply that a copy constructor is invoked.
-- End edit.
The function result can then be received into an rvalue reference to prevent copying the temporary to a local:
C && x = factory(); // applies to other initialization syntax
The question is, how to initialize a nonstatic member from a factory function returning non-copyable, non-moveable type? The reference trick doesn't work because a reference member doesn't extend the lifetime of a temporary.
Note, I'm not considering aggregate-initialization. This is about defining a constructor.
On your main question:
The question is, how to initialize a nonstatic member from a factory function returning non-copyable, non-moveable type?
You don't.
Your problem is that you are trying to conflate two things: how the return value is generated and how the return value is used at the call site. These two things don't connect to each other. Remember: the definition of a function cannot affect how it is used (in terms of language), since that definition is not necessarily available to the compiler. Therefore, C++ does not allow the way the return value was generated to affect anything (outside of elision, which is an optimization, not a language requirement).
To put it another way, this:
C c = {...};
Is different from this:
C c = [&]() -> C {return {...};}()
You have a function which returns a type by value. It is returning a prvalue expression of type C. If you want to store this value, thus giving it a name, you have exactly two options:
Store it as a const& or &&. This will extend the lifetime of the temporary to the lifetime of the control block. You can't do that with member variables; it can only be done with automatic variables in functions.
Copy/move it into a value. You can do this with a member variable, but it obviously requires the type to be copyable or moveable.
These are the only options C++ makes available to you if you want to store a prvalue expression. So you can either make the type moveable or return a freshly allocated pointer to memory and store that instead of a value.
This limitation is a big part of the reason why moving was created in the first place: to be able to pass things by value and avoid expensive copies. The language couldn't be changed to force elision of return values. So instead, they reduced the cost in many cases.
Issues like this were among the prime motivations for the change in C++17 to allow these initializations (and exclude the copies from the language, not merely as an optimization).