Using move semantics with std::pair or std::tuple - c++

Suppose you want to take advantage of move semantics, but one of your movable classes needs to be part of an std::pair. The purpose would be to create a function that returns an std::pair that can be treated as an rvalue, and forwarded along.
But I can't see how this can be done, unless an internal change to std::pair itself is made, to make it aware of move semantics.
Consider the following code:
struct Foo
{
Foo() { }
Foo(Foo&& f) { }
private:
Foo(const Foo& f) { } // do not allow copying
};
int main()
{
Foo f;
std::pair<Foo, int> res = std::make_pair(f, 10); // fails due to private copy constructor
}
The problem is that std::make_pair, as well as the std::pair constructor itself, takes two objects and tries to make internal copies of them. This causes it to try and invoke the copy constructor. But in my example, I want to be able to move the new pair into res, and ensure that no copies are made. I would think this wouldn't be possible unless std::pair itself had the following constructor defined internally:
pair(T1&& t1, T2&& t2) : first(std::move(t1)), second(std::move(t2))
But it doesn't, at least not on the compiler I'm using (gcc 4.3.2). It may be that my compiler is simply out-of-date, and newer versions in fact will have this move-aware constructor. But my understanding of move semantics is somewhat flaky at the moment, so I'm not sure if I'm simply overlooking something here. So, is what I'm trying to accomplish possible, without actually reimplementing std::pair? Or is my compiler just out of date?

That's not the std::pair constructor that will be called, though. The std::pair move constructor will be called, and the move constructor should do exactly what you expect (N3126 20.3.5.2/6):
template<class U, class V> pair(pair<U, V>&& p);
Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).
However, your example should fail because in std::make_pair(f, 10);, f is an lvalue and needs to be explicitly moved, otherwise it is copied. The following should work:
std::pair<Foo, int> res = std::make_pair(std::move(f), 10);

GCC 4.3.2 must not have a complete implementation. pair (and tuple) shall have move constructors:
template<class U, class V> pair(U&& x, V&& y);
Effects: The constructor initializes first with std::forward(x) and second with std::forward(y).
template<class U, class V> pair(pair<U, V>&& p);
Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).
(From [pairs.pair] in n3126)

Related

Copy constructor called endlessly from lambda capture group

I wrote the following class to create values of any type which are either fixed or recalculated everytime the call operator is used on them:
template <typename T>
class DynamicValue {
private:
std::variant<T, std::function<T()>> getter;
public:
DynamicValue(const T& constant) : getter(constant){};
template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
DynamicValue(F&& function) : getter(function) {}
DynamicValue(const T* pointer) : DynamicValue([pointer]() { return *pointer; }) {}
DynamicValue(const DynamicValue& value) : getter(value.getter) {}
DynamicValue(DynamicValue& value) : DynamicValue((const DynamicValue&) value) {}
~DynamicValue() {}
T operator()() const { return getter.index() == 0 ? std::get<T>(getter) : std::get<std::function<T()>>(getter)(); }
};
I also wrote this function, which takes a DynamicValue<int> and returns another DynamicValue<int> which returns its value plus 1:
DynamicValue<int> plus1(DynamicValue<int> a) {
return [a] { return a() + 1; };
}
However, when I attempt to do use it, the program crashes:
DynamicValue<int> a = 1;
DynamicValue<int> b = plus1(a);
You can try a live example here.
After some testing, I think the problem lies in the copy constructor, which is being called endlessly, but I'm not sure how to fix it. How can I avoid this behavior?
Some important pieces of this code:
A lambda captures a DynamicValue object by value (copy).
The lambda is used to initialize a std::variant as a std::function alternative.
There is no explicit move constructor for DynamicValue, so the template for invocable objects is used as the move constructor.
The problematic code path starts with the request to construct a DynamicValue object from the lambda. This invokes the template constructor, which attempts to copy the lambda into the std::function alternative of the variant. So far, so good. Copying (not moving) the lambda copies the captured object without problems.
However, this procedure works when the CopyConstructible named requirement is satisfied. Part of this named requirement is being MoveConstructible. In order for a lambda to satisfy MoveConstructible, all of its captures have to satisfy that named requirement. Is this the case for DynamicValue? What happens when your standard library tries to move the lambda (hence also the captured object), with copying as the fallback? While DynamicValue has no explicit move constructor, it is invocable...
When F is DynamicValue<T>, the template constructor serves as the move constructor. It tries to initialize the variant by converting the source DynamicValue (the captured copy of a in the question's code) into a std::function. This is allowed, a copy of the source is made, and the process continues until the copy needs to be moved, at which point the move constructor is again invoked. This time, it tries to initialize the variant by converting the copy of the source DynamicValue into a std::function. This is allowed, a copy of the copy of the source is made, and the process continues until the copy of the copy needs to be moved, at which point the move constructor is again invoked. Etc.
Instead of moving the DynamicValue into the new object, each "move constructor" tries to move the DynamicValue into the variant of the new object. This would add another layer of overhead with each move, except the recursive calls blow up before construction finishes.
The solution is to make DynamicValue move constructible. There are at least two ways to do this.
1) Explicitly provide a move constructor.
DynamicValue(DynamicValue&& value) : getter(std::move(value.getter)) {}
2) Exclude DynamicValue from being a template argument to the template constructor.
template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, DynamicValue>>>
DynamicValue(F&& function) : getter(function) {}
Note that this excludes only DynamicValue<T> from being a template argument, not DynamicValue<U> when U is not T. That might be another issue to contemplate.
You might want to see if this also fixes whatever problem led you to define a second copy constructor. That may have been a band-aid approach that did not address this underlying issue.

Perfect forwarding and constructors

I am trying to understand the interaction of perfect forwarding and constructors. My example is the following:
#include <utility>
#include <iostream>
template<typename A, typename B>
using disable_if_same_or_derived =
std::enable_if_t<
!std::is_base_of<
A,
std::remove_reference_t<B>
>::value
>;
template<class T>
class wrapper {
public:
// perfect forwarding ctor in order not to copy or move if unnecessary
template<
class T0,
class = disable_if_same_or_derived<wrapper,T0> // do not use this instead of the copy ctor
> explicit
wrapper(T0&& x)
: x(std::forward<T0>(x))
{}
private:
T x;
};
class trace {
public:
trace() {}
trace(const trace&) { std::cout << "copy ctor\n"; }
trace& operator=(const trace&) { std::cout << "copy assign\n"; return *this; }
trace(trace&&) { std::cout << "move ctor\n"; }
trace& operator=(trace&&) { std::cout << "move assign\n"; return *this; }
};
int main() {
trace t1;
wrapper<trace> w_1 {t1}; // prints "copy ctor": OK
trace t2;
wrapper<trace> w_2 {std::move(t2)}; // prints "move ctor": OK
wrapper<trace> w_3 {trace()}; // prints "move ctor": why?
}
I want my wrapper to have no overhead at all. In particular, when marshalling a temporary into the wrapper, as with w_3, I would expect the trace object to be created directly in place, without having to call the move ctor. However, there is a move ctor call, which makes me think a temporary is created and then moved from. Why is the move ctor called? How not to call it?
I would expect the trace object to be created directly in place,
without having to call the move ctor.
I don't know why you expect that. Forwarding does exactly this: moves or copies 1). In your example you create a temporary with trace() and then the forwarding moves it into x
If you want to construct a T object in place then you need to pass the arguments to the construction of T, not an T object to be moved or copied.
Create an in place constructor:
template <class... Args>
wrapper(std::in_place_t, Args&&... args)
:x{std::forward<Args>(args)...}
{}
And then call it like this:
wrapper<trace> w_3 {std::in_place};
// or if you need to construct an `trace` object with arguments;
wrapper<trace> w_3 {std::in_place, a1, a2, a3};
Addressing a comment from the OP on another answer:
#bolov Lets forget perfect forwarding for a minute. I think the
problem is that I want an object to be constructed at its final
destination. Now if it is not in the constructor, it is now garanteed
to happen with the garanteed copy/move elision (here move and copy are
almost the same). What I don't understand is why this would not be
possible in the constructor. My test case proves it does not happen
according to the current standard, but I don't think this should be
impossible to specify by the standard and implement by compilers. What
do I miss that is so special about the ctor?
There is absolutely nothing special about a ctor in this regard. You can see the exact same behavior with a simple free function:
template <class T>
auto simple_function(T&& a)
{
X x = std::forward<T>(a);
// ^ guaranteed copy or move (depending on what kind of argument is provided
}
auto test()
{
simple_function(X{});
}
The above example is similar with your OP. You can see simple_function as analog to your wrapper constructor and my local x variable as analog to your data member in wrapper. The mechanism is the same in this regard.
In order to understand why you can't construct the object directly in the local scope of simple_function (or as data member in your wrapper object in your case) you need to understand how guaranteed copy elision works in C++17 I recommend this excelent answer.
To sum up that answer: basically a prvalue expression doesn't materializez an object, instead it is something that can initialize an object. You hold on to the expression for as long as possible before you use it to initialize an object (thus avoiding some copy/moves). Refer to the linked answer for a more in-depth yet friendly explanation.
The moment your expression is used to initialize the parameter of simple_foo (or the parameter of your constructor) you are forced to materialize an object and lose your expression. From now on you don't have the original prvalue expression anymore, you have a created materialized object. And this object now needs to be moved into your final destination - my local x (or your data member x).
If we modify my example a bit we can see guaranteed copy elision at work:
auto simple_function(X a)
{
X x = a;
X x2 = std::move(a);
}
auto test()
{
simple_function(X{});
}
Without elision things would go like this:
X{} creates a temporary object as argument for simple_function. Lets call it Temp1
Temp1 is now moved (because it is a prvalue) into the parameter a of simple_function
a is copied (because a is an lvalue) into x
a is moved (because std::move casts a to an xvalue) to x2
Now with C++17 guaranteed copy elision
X{} no longer materializez an object on the spot. Instead the expression is held onto.
the parameter a of simple_function can now by initialized from the X{} expression. No copy or move involved nor required.
The rest is now the same:
a is copied into x1
a is moved into x2
What you need to understand: once you have named something, that something must exist. The surprisingly simple reason for that is that once you have a name for something you can reference it multiple times. See my answer on this other question. You have named the parameter of wrapper::wrapper. I have named the parameter of simple_function. That is the moment you lose your prvalue expression to initialize that named object.
If you want to use the C++17 guaranteed copy elision and you don't like the in-place method you need to avoid naming things :) You can do that with a lambda. The idiom I see most often, including in the standard, is the in-place way. Since I haven't seen the lambda way in the wild, I don't know if I would recommend it. Here it is anyway:
template<class T> class wrapper {
public:
template <class F>
wrapper(F initializer)
: x{initializer()}
{}
private:
T x;
};
auto test()
{
wrapper<X> w = [] { return X{};};
}
In C++17 this grantees no copies and/or moves and that it works even if X has deleted copy constructors and move constructors. The object will be constructed at it's final destination, just like you want.
1) I am talking about the forwarding idiom, when used properly. std::forward is just a cast.
A reference (either lvalue reference or rvalue reference) must be bound to an object, so when the reference parameter x is initialized, a temporary object is required to be materialized anyway. In this sense, perfect forwarding is not that "perfect".
Technically, to elide this move, the compiler must know both the initializer argument and the definition of the constructor. This is impossible because they may lie in different translation units.

Compilation error when returning an std::map of implicitly non-copyable structs on new versions of gcc

I get this strange compilation error on new versions of gcc (4.9+).
Here is the code:
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
using namespace std;
struct ptrwrap
{
unique_ptr<int> foo;
};
template <typename T>
struct holder
{
holder() = default;
holder(const holder& b)
: t(b.t)
{
}
holder(holder&& b)
: t(std::move(b.t))
{
}
holder& operator=(const holder& h)
{
t = h.t;
return *this;
}
holder& operator=(holder&& h)
{
t = std::move(h.t);
return *this;
}
T t;
};
struct y_u_no_elision
{
holder<ptrwrap> elem;
};
typedef map<std::string, y_u_no_elision> mymap;
mymap foo();
int main()
{
auto m = foo();
m = foo();
return 0;
}
Here it is on ideone too with the actual error. Basically it boils down to using the deleted copy constructor of ptrwrap. Which... shouldn't happen. The map is returned by value (ie moved), so no copies can exist.
Now the same code is compiled with no problems on older versions of gcc (I tried 4.2 and 4.3), all versions of clang I tried, and also Visual Studio 2015.
Curiously if I remove the explicit copy and move constructors of the holder template, it also compiles on gcc 4.9+. If I change the map to a vector or an unordered_map it also compiles fine (here is a link to a compiling version of the code with unordered_map)
So... is this a gcc 4.9 bug or are the other compilers being permissive of something I can't see? Is there anything I can do about this which doesn't involve changing the holder class?
Short answer: It's a bug in libstdc++. According to the allocator-aware container requirements table in [container.requirements.general] in the Standard (hasn't changed since C++11), container move assignment:
Requires: If allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
is false, T is MoveInsertable into X and MoveAssignable.
[...]
(X is the container type and T is its value_type)
You're using the default allocator, which has using propagate_on_container_move_assignment = true_type;, so the above requirement doesn't apply; there should be no special requirements on the value_type.
Quick fix: If you cannot touch holder, one solution is to change y_u_no_elision, adding
y_u_no_elision(const y_u_no_elision&) = delete;
y_u_no_elision(y_u_no_elision&&) = default;
Long story: The bug is essentially caused by this line in stl_tree.h.
_Rb_tree is the underlying implementation of std::map and that line in its move assignment operator definition basically does the check specified by the Standard quote above. However, it does it using a simple if, which means that, even if the condition is satisfied, the other branch has to compile as well, even though it won't be executed at runtime. Lacking the shiny new C++17 if constexpr, this should be implemented using something like tag dispatching (for the first two conditions - the third one is a true runtime check), in order to avoid instantiating the code outside the taken branch.
The error is then caused by this line, which uses std::move_if_noexcept on value_type. And here comes the long story.
value_type is std::pair<const std::string, y_u_no_elision>.
In your initial code:
holder has non-deleted, non-noexcept copy and move constructors.
This means the corresponding implicitly-declared constructors of y_u_no_elision will also be non-deleted and non-noexcept.
These characteristics propagate to the constructors of value_type.
This results in std::move_if_noexcept returning const value_type& instead of value_type&& (it falls back to copy if it can - see these docs).
That eventually causes the copy constructor of y_u_no_elision to be called, which will cause holder<ptrwrap>'s copy constructor definition to be instantiated, which tries to copy a std::unique_ptr.
Now, if you remove the user-declared copy and move constructors and assignment operators from holder:
holder will get the implicitly-declared ones. The copy constructor will be deleted and the move constructor will be defaulted, not deleted and noexcept.
This propagates up to value_type, with one exception unrelated to holder: value_type's move constructor will try to move from a const std::string; this will not call string's move constructor (which is noexcept in this case), but rather its copy constructor, as string&& cannot bind to an rvalue of type const string.
string's copy constructor is not noexcept (it may have to allocate memory), so value_type's move constructor won't be either.
So, why does the code compile? Because of the logic behind std::move_if_noexcept: it returns an rvalue reference even if the argument's move constructor isn't noexcept, as long as the argument is not copy-constructible (it falls back to non-noexcept move if it cannot fall back to copy); and value_type isn't, because of holder's deleted copy constructor.
This is the logic behind the quick fix above: you have to do something to make value_type have a valid move constructor and a deleted copy constructor, in order to get an rvalue reference from move_if_noexcept. This is because you won't be able to make value_type have a noexcept move constructor due to the const std::string, as explained above.
The holder copy constructor invokes t copy constructor:
holder(const holder& b)
: t(b.t)
{
}
If t is a unique_ptr then it doesn't support a construction by copy.

How to know or test if a given type is going to be moved

I'm not looking for a type trait for movable types, nor rules for automatic generation of move operations. What I'm looking for is a general guide to know if a given type is going to be moved or copied, or a way to figure it myself with testing.
In some cases, the moving operation is performed without the user ever noticing, for example:
void f(std::string) { ... }
void f_c(const std::string) { ... }
void g()
{
f(std::string("Hello world!")); // moved, isn't it?
f("Hello world!"); // moved, isn't it?
f_c(std::string("Hello world!")); // moved, isn't it?
f_c("Hello world!"); // moved, isn't it?
}
Prior to C++11 the code above would incurr in std::string copy from a temporary value to the one passed to f and f_c, from C++11 onwards the std::basic_string provides a move constructor (see here (8)) and the temporary created is moved into the parameter passed to f and f_c.
Sometimes the users were trying to force the move semantics with the std::move function:
std::string return_by_value_1()
{
std::string result("result);
return std::move(result); // Is this moved or not?
}
std::string return_by_value_2()
{
return std::move(std::string("result)); // Is this moved or not?
}
But std::move doesn't move anything1: it only converts lvalues into rvalues references, if the target type doesn't implement move semantics: no move operation is performed... and AFAIK std::moveing in the return_by_value_x functions above prevents the compiler from doing RVO (we're worsening the code!).
So, after the (maybe unnecessary) introduction, I'll ask my questions:
How to know or test if a given type is going to be moved or copied?
This question is about basic types and complex ones:
int f_int(int) { ... };
template <typename F, typename S> void f_pair(std::pair<F, S>) { ... };
struct weird
{
int i;
float f;
std::vector<double> vd;
using complexmap = std::map<std::pair<std::string, std::uint64_t>, std::pair<std::uint32_t, std::uint32_t>>;
complexmap cm;
};
struct silly
{
std::vector<std::pair<const std::string, weird::complexmap>> vcm;
};
f_weird(weird) { ... };
f_silly(silly) { ... };
A basic type can be moved? there's some calls to f_int which implies a move operation?
f_int(1); // this moves or construct an int in-place?
f_int(1 + 2); // this moves or construct an int in-place?
f_int(f_int(1) + 2); // this moves or construct an int in-place?
A complex type with const members can't be moved, isn't it?
f_pair<std::pair<const std::string, int>>({"1", 2}); // unmovable?
f_pair<std::pair<std::string, std::string>>({"1", "2"}); // this moves?
f_silly({{{}, {}}}); // this moves?
The struct weird is moveable?
f_weird({1, .0f, {0.d, 1.d}, {{{"a", 0ull}, {1u, 2u}}}}) // this moves?
How to test by myself the above cases in order to determine if a given type is being moved in a given context?
1Wouldn't be better if it was called std::rvalref or something similar?
How to test by myself the above cases in order to determine if a given
type is being moved in a given context?
You can test whether a derived class would define a defaulted move constructor as deleted via SFINAE. This idea does not work for final classes. A rough sketch that seems to work is
namespace detail {
// No copy constructor. Move constructor not deleted if T has an applicable one.
template <typename T>
struct Test : T { Test(Test&&) = default; };
}
// TODO: Implement unions
template <typename T>
using is_moveable = std::is_move_constructible<
std::conditional_t<std::is_class<T>{}, detail::Test<T>, T>>;
Demo.
The move constructor of Test is a defaulted move constructor according to [dcl.fct.def.default]/5. The relevant quote that makes the above work is then in [class.copy]/11:
A defaulted [..] move constructor for a class X is defined as
deleted (8.4.3) if X has:
a potentially constructed subobject type M (or array thereof) that cannot be [..] moved because overload resolution (13.3), as
applied to M’s corresponding constructor, results in an ambiguity or
a function that is deleted or inaccessible from the defaulted
constructor, [..]
A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).
For the initialization
Test(Test())
to be valid, as the copy ctor is not implicitly declared, a move constructor must be available. However, according to the above quote, Test's move constructor will be defined as deleted (and thus ignored by overload resolution) if the base class, our given T, does not have an invokeable move constructor. (I am slightly unsure about the case where T does not have any move constructor, I'd be grateful for clarification on the standard there.)

Code calls copy ctor where it normally had to call move ctor

What i have is basically a std::map holding pointers to Views.
std::map<string,View*> myViews;
template<typename T>
bool addView( string assocName , T&& view )
{
typedef typename std::remove_reference<T>::type T2;
typedef typename T2::View dummy; // check if T is subclass of 'View'
// Remove any Current View
View* &data = myViews[assocName]; // reference to pointer
if( data )
delete data; // Delete Current associated view
// Insert the new View
data = new T2( std::move(view) ); // <-- Here is the error
}
'addView' is called the following way:
viewSwitcher.addView( "3" , ScSetupPage3() );
My problem is that the class 'ScSetupPage3' doesn't have a copy ctor, but 'addView' tries to call it!?
This is the error msg, my GNU GCC gives me:
error: use of deleted function 'ScSetupPage3::ScSetupPage3(const ScSetupPage3&)'
Solution:
ScSetupPage3 doesn't have a default move ctor because it has a non-primitive ctor declared. Hence it will be copied and not moved in lack of an appropriate ctor, even if its members could be moved or even have a move-ctor declared.
std::move is what you are looking for. It is basically a cast to an rvalue if possible.
From an implementation I found, it seems you only did a small mistake, by trying to do the cast yourself:
template<class T>
typename remove_reference<T>::type&&
std::move(T&& a) noexcept
{
typedef typename remove_reference<T>::type&& RvalRef;
return static_cast<RvalRef>(a);
}
As juanchopanza asked whether ScSetupPage3 had a move ctor declared. I saw, that it indeed hadn't one:
ScSetupPage3 doesn't have a default move ctor because it has a non-primitive ctor declared. Hence it will be copied and not moved in lack of an appropriate ctor, even if its members could be moved or even have a move-ctor declared.