Should all/most setter functions in C++11 be written as function templates accepting universal references?
You know the classes A and B, so you know if they are movable or not and if this design is ultimately necessary. For something like std::string
, it's a waste of time changing the existing code unless you know you have a performance problem here. If you're dealing with auto_ptr
, then it's time to rip it out and use unique_ptr
.
It's usually preferred now to take arguments by value if you don't know anything more specific- such as
void set_a(A a) { _a = std::move(a); }
This permits the use of any of the constructors of A
without requiring anything except movability and offers a relatively intuitive interface.
Should all C++ functions be declared taking rvalue from now?
Various stackexchange questions have helped me answer my own question.
I had forward and move confused. This answer helped (Can I typically/always use std::forward instead of std::move?) You can r/forward/move/g and my question is still the same. Be sure to read Scott Meyer's response to this question directly: http://scottmeyers.blogspot.co.uk/2012/11/on-superfluousness-of-stdmove.html
So is it a good idea? Well, these posts helped me figure that out: Is pass-by-value a reasonable default in C++11?
And
Should all/most setter functions in C++11 be written as function templates accepting universal references?
And
Should I always move on `sink` constructor or setter arguments?
C++ - Functions calling setters - how to decorate the arguments?
When you have a value you are not going to reuse, std::move
it.
When you have an rvalue reference you are not going to reuse, std::move
it.
When a forwarding reference, std::forward
it if and only if you'd move it if it was an rvalue reference.
The rule to take by value presumes:
You don't mind silently making a copy at the call site
Moving is so cheap you treat it as free
Copying is much more expensive than moving
If (1) is false, instead take an rvalue reference (A&&
) in your sink parameter. Now callers have to explicitly make their copy.
If (2) is false, perfect forwarding, multiple overloads, type-erased emplacing, or a myriad of other more complex solutions should be used.
If (3) is false, don't bother with move
ing. Just take by value.
If all 3 are true, then your set_ab
looks like:
void set_ab(A a, B b) {
set_a(std::move(a));
set_b(std::move(b));
}
Is overloading on universal references now much safer with concepts in c++ 20
I would say no. I mean, concepts help, because the syntax is nicer than what we had before, but it's still the same problem.
Here's a real-life example: std::any
is constructible from any type that is copy constructible. So there you might start with:
struct any {
template <class T>
requires std::copy_constructible<std::decay_t<T>>
any(T&&);
any(any const&);
};
The problem is, when you do something like this:
any a = 42; // calls any(T&&), with T=int
any b = a; // calls any(T&&), with T=any
Because any
itself is, of course, copy constructible. This makes the constructor template viable, and a better match since it's a less-const-qualified reference.
So in order to avoid that (because we want b
to hold an int
, and not hold an any
that holds an int
) we have to remove ourselves from consideration:
struct any {
template <class T>
requires std::copy_constructible<std::decay_t<T>>
&& (!std::same_as<std::decay_t<T>, any>)
any(T&&);
any(any const&);
};
This is the same thing we had to do in C++17 and earlier when Scott Meyers wrote his book. At least, it's the same mechanism for resolving the problem - even if the syntax is better.
Template function for applying args to function
template <typename Func, typename ...Args>
decltype(auto) apply_args(Func &&f, Args &&...args) {
return f(std::forward<Args>(args)...);
}
Best way to create a setter function in C++
The two setter versions setA(A&& a)
and setA(const A& a)
can be combined into a single one using a forwarding reference (a.k.a. perfect forwarding):
template<typename A>
void setA(A&& a)
{
m_a = std::forward<A>(a);
}
The compiler will then synthesize either the rvalue- or lvalue-reference version as needed depending on the value category.
This also solves the issue of multi-value setters, as the right one will be synthesized depending on the value category of each parameter.
Having said that, keep in mind that setters are just regular functions; the object is technically already constructed by the time any setter can be called. In case of setA
, if A
has a non-trivial constructor, then an instance m_a
would already have been (default-)constructed and setA
would actually have to overwrite it.
That's why in modern C++, the focus is often not so much on move- vs. copy-, but on in-place construction vs. move/copy.
For example:
struct A {
A(int x) : m_x(x) {}
int m_x;
};
struct B {
template<typename T>
B(T&& a) : m_a(std::forward<T>(a)) {}
A m_a;
};
int main() {
B b{ 1 }; // zero copies/moves
}
The standard library also often offers "emplace"-style calls in addition to more traditional "push"/"add"-style calls. For example, vector::emplace
takes the arguments needed to construct an element, and constructs one inside the vector, without having to copy or move anything.
Related Topics
C++ Objects: When Should I Use Pointer or Reference
C++ Push Multiple Types Onto Vector
Default Move Constructor/Assignment and Deleted Copy Constructor/Assignment
How Does an Extern "C" Declaration Work
Glpixelstorei(Gl_Unpack_Alignment, 1) Disadvantages
Windows & C++: Extern & _Declspec(Dllimport)
How to Get the Type of a Lambda Argument
One Way of Eliminating C4251 Warning When Using Stl-Classes in the Dll-Interface
Missing C++ Header <_Debug> After Updating Osx Command Line Tools 6.3
Why Is (Void) 0 a No Operation in C and C++
"Apientry _Twinmain" and "Winapi Winmain" Difference
What's the Point in Defaulting Functions in C++11
Iterating Through a Lua Table from C++