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.
Related
The constructor of std::function looks like this (at least in libc++):
namespace std {
template<class _Rp, class ..._ArgTypes>
function {
// ...
base_func<_Rp(_ArgTypes...)> __func;
public:
template<typename _Fp>
function(_Fp __f) : __func(std::move(__f)) {}
template<typename _Fp>
function& operator=(_Fp&& __f) {
function(std::forward<_Fp>(__f)).swap(*this);
return *this;
}
};
}
It provides a constructor from an arbitrary functor and an assignment operator from an arbitrary functor. The constructor uses pass-by-value but the assignment operator uses pass-by-universal-reference.
My question is why the constructor of std::function doesn't pass by universal (forwarding) reference in the same way as the assignment operator? For example, it could do:
namespace std {
template<class _Rp, class ..._ArgTypes>
function {
// ...
base_func<_Rp(_ArgTypes...)> __func;
public:
template<typename _Fp>
function(_Fp&& __f) : __func(std::forward<_Fp>(__f)) {}
template<typename _Fp>
function& operator=(_Fp&& __f) {
function(std::forward<_Fp>(__f)).swap(*this);
return *this;
}
};
}
I am curious what the rationale behind treating assignment and constructor differently here. Thanks!
That is what it's called a "sink parameter". A sink parameter is a parameter of a method that needs to be "taken" from the caller and stored in the object (as a data member). The caller usually doesn't need/use the the object after the call.
The best practice for a sink parameter is to pass it by value and move from it into the object. Let's see why:
Option 1: pass by reference
class X; // expensive to copy type with cheap move
struct A
{
X stored_x_;
A(const X& x) : x_{x} {}
// ^~~~~
// this is always a copy
};
In this case there will always be at least 1 copy that cannot be elided.
Option 2: pass by value and then move from
class X; // expensive to copy type with cheap move
struct A
{
X stored_x_;
A(X x) : x_{std::move(x)} {}
// ^~~~~~~~~~~~~~~~
// this is now a move
};
We got rid of the move in the initialization of A::x_, but we still have a copy on passing the parameter, or do we?
If the caller does the right thing we don't. We have two cases here: The caller still needs a copy of the object passed (which is pretty unusual and non-idiomatic). In this case yes, a copy will be made, but that is because the called requires that, not because of a flaw in the design of our class A.
The caller doesn't need the object after passing it. In this case it moves the argument or better yet passes a prvalue and since C++17 with the new temporary materialization rules the object is created directly as a parameter:
Pass an xvalue
auto test()
{
X x{};
A a{std::move(x)}; // 2 moves (from arg to parameter and from parameter to `A::x_`)
};
Pass an prvalue
auto test()
{
A a{X{}}; // just the move in the initialization of `A::x_`
}
Option 3: lvalue and rvalue reference overloads
Yes, this will achieve the same level of performance, but why have 2 overloads when you can write and maintain just 1 method.
class X; // expensive to copy type with cheap move
struct A
{
X stored_x_;
A(const X& x) : x_{x} {}
A(X&& x) : x_{std::move(x)} {}
};
Unneeded complexity which blows out when you have multiple sink parameters in 1 method.
Option 4: pass by forwarding reference:
Again, possible. But it can have some subtle but pretty serios problems:
if you don't have a template parameter then you need to make it a template, which adds complexity and also adds other problems like now the method accepts any type.
This is even worse for a constructor as now this constructor is a viable option for a copy constructor which can really mess things up because this will be a better fit for a copy from a non-const object.
Another problem is that it cannot be always used:
if you want to accept any type that is not simply T, e.g. if X is templated: template <class T> A(X<T>&& x) this is not a forwarding reference, but an rvalue reference and you need an lvalue reference overload.
Suppose I have a class ThisHasAStringMember. Assume it has a private member which is a string, and I want to efficiently get the string value, preferring a move over a copy where possible. Would the following two constructors accomplish that?
class ThisHasAStringMember
{
public:
// ctors
ThisHasAStringMember(const std::string str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
// getter (no setter)
std::string value() { return m_str; }
private:
std::string m_str;
}
Do I need the double ampersand before the str parameter in the second constructor?
Is this the right way to accomplish this?
At first I would notice that it's better to mark your constructors as explicit.
The next moment is that better change the first constructor in your solution to take const reference to avoid copying lvalue:
// ctors
ThisHasAStringMember(const std::string& str) : m_str(str) {}
ThisHasAStringMember(std::string &&str) : m_str(std::move(str)) {}
This approach is optimal from the performance point of view (you will have one copy constructor call for lvalue and one move constructor call for rvalue), however it's quite boring to implement each time two constructors in such case. And if you have N members - 2^N constructors.
There are few alternatives:
Signle constructor where you pass parameter just by value. Yes it was unefficient in C++98, but in C++11 when you create a full copy - that's an option.
ThisHasAStringMember(std::string str) : m_str(std::move(str)) {}
When lvalue is passed there will be one copy constructor call and one move constructor call. When rvalue is passed there will be two move constructor calls. Yes, you have one extra move constructor call in each of the cases. But it's often very cheap (or even can be optimized away by compiler) and the code is very simple.
Single constructor where you pass parameter by rvalue:
ThisHasAStringMember(std::string&& str) : m_str(std::move(str)) {}
If you pass lvalue you have to explicitely copy it first in a place of the call, e.g.
ThisHasAStringMember(copy(someStringVar)). (here copy is a simple template copying method). And you will still have one extra move constructor call for lvalues. For rvalues there will be no overhead. Personally I like this approach: all the places where the parameter is copied are explicit, you won't make occasional copies in a performance-critical places.
Make constructor template and use perfect forwarding:
template <typename String,
std::enable_if_t<std::is_constructible_v<std::string, String>>* = nullptr>
ThisHasAStringMember(String&& str) : m_str(std::forward<String>(str))
{}
You will have no overhead both for rvalues and lvalues, but you'll need to make your constructor template and define it in header in most of the cases.
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.
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.)
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)