Minimum syntax to guarantee no dynamic memory with std::function - c++

Quite often I want to write higher order functional code like
void f(int value, const std::function<void(int)>& callback);
int x, y=5;
f(y, [&](int result) { x = result; });
In cases like these, I would like to be able to guarantee that the std::function constructor does not allocate any memory. The guarantees in the spec are... hard to read. There seems to be some guarantees surrounding reference_wrapper, but I have not been able to get them to work cleanly, due to what I think are lvalue vs rvalue issues. I end up with
auto callback = [&](int result) { x = result; };
f(y, std::ref(callback));
In many of these cases, I want to leverage virtual functions, so I can't just template these issues away (although I have played with using a wrapper that accepts the lambda type as an argument, and wraps it with std::ref, sidestepping any issues regarding temporaries)
What is the minimum amount of syntactic boilerplate needed to ensure this pattern does not allocate any memory?

There are no guarantees of allocations (or lack of thereof) specified in the standard for std::function constructors. Most you can hope for is a recommendation, from 20.14.17.3.2:
Recommended practice: Implementations should avoid the use of
dynamically allocated memory for small callable objects, for example,
where f refers to an object holding only a pointer or reference to an
object and a member function pointer.
So your best bet would be to look at your implementation and check when allocation does not happen.

Related

Modern C++ approach for providing optional arguments

Let's take the following function declaration:
void print(SomeType const* i);
Here, the const* nature of the argument i suggest the intent, that the parameter is optional, since it may be nullptr. If this was not intended, the argument would instead just be a const&. Communicating optional-semantics were certainly not the original intent for designing pointers, but using them to do so happens to work just fine for a long time.
Now, since using raw pointers is generally discouraged in modern C++ (and should be avoided in favor of std::unique_ptr and std::shared_ptr to precisely indicate particular ownership-semantics), I wonder how to properly indicate function parameters' optional-semantics without passing by value, i. e. copying, as
void print(std::optional<SomeType> i);
would do.
After thinking about it for a while I came up with the idea of using:
void print(std::optional<SomeType const&> i);
This would in fact be most precise. But it turns out that std::optional cannot have reference types.¹
Also, using
void print(std::optional<SomeType> const& i);
would in no way be optimal, since then we would require our SomeType to exists in an std::optional on the caller-side, again possibly (or rather likely) requiring a copy there.
Question: So what would be a nice modern approach for allowing optional arguments without copying? Is using a raw pointer here still a reasonable approach in modern C++?
¹: Ironically the depicted reason for why std::optional cannot have reference types (controversy about rebinding or forwarding on assignment) does not apply in the case of std::optionals of const references, since they cannot be assigned to.
Accepting a raw pointer is perfectly fine and is still done in plenty of "modern" codebases (which I'll note is a fast-moving target). Just put a comment on the function saying that it's allowed to be null and whether the function holds a copy of the pointer after the call (i.e. what are the lifetime requirements for the pointed-to value).
Does function overloading provide a clean solution here? E.g. To declare both the const ref and empty param list versions of the function?
This may depend on what the function body does in the no argument/null case - and how you can manage the two implementations to minimize code overlap.
Raw pointers are usually fine for this type of optional argument passing, actually one of the only times it is fine to use raw pointers overall. This is also the canonical recommended way.
That being said, boost::optional does allow you to use reference optional and const reference optionals. It was decided against to have this feature in the std library (for reasons I leave out here).
This is actually what std::reference_wrapper was made for. Also see Does it make sense to combine optional with reference_wrapper? for more reasoning as to when to use it, and when not to use it.
Here, the const* nature of the argument i suggest the intent, that the parameter is optional since it may be nullptr.
[...]
So what would be a nice modern approach for allowing optional arguments without copying?
Allowing an optional argument (not in the std::optional sense, but in the semantic sense) with differing implementation variations based on whether the optional argument is present or not sound like an ideal candidate for overloading:
struct SomeType { int value; };
namespace detail {
void my_print_impl(const SomeType& i) {
std::cout << i.value;
}
} // namespace detail
void my_print() {
const SomeType default_i{42};
detail::my_print_impl(default_i);
}
void my_print(const SomeType& i) {
detail::my_print_impl(i);
}
or
namespace detail {
void my_print_impl() {
std::cout << "always print me\n";
}
} // namespace detail
void my_print() {
detail::my_print_impl();
}
void my_print(const SomeType& i) {
detail::my_print_impl();
std::cout << "have some type: " << i.value;
}
or some similar variation, depending on what your implementation should do depending on the existence/non-existence of the optional argument.
Optional references, otherwise, are basically raw pointers, and the latter may just as well be used (if overloading is not applicable).

in c++11, is it necessary to provide rvalue overrides for functions move-assigning large objects? [duplicate]

Since we have move semantics in C++, nowadays it is usual to do
void set_a(A a) { _a = std::move(a); }
The reasoning is that if a is an rvalue, the copy will be elided and there will be just one move.
But what happens if a is an lvalue? It seems there will be a copy construction and then a move assignment (assuming A has a proper move assignment operator). Move assignments can be costly if the object has too many member variables.
On the other hand, if we do
void set_a(const A& a) { _a = a; }
There will be just one copy assignment. Can we say this way is preferred over the pass-by-value idiom if we will pass lvalues?
Expensive-to-move types are rare in modern C++ usage. If you are concerned about the cost of the move, write both overloads:
void set_a(const A& a) { _a = a; }
void set_a(A&& a) { _a = std::move(a); }
or a perfect-forwarding setter:
template <typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
that will accept lvalues, rvalues, and anything else implicitly convertible to decltype(_a) without requiring extra copies or moves.
Despite requiring an extra move when setting from an lvalue, the idiom is not bad since (a) the vast majority of types provide constant-time moves and (b) copy-and-swap provides exception safety and near-optimal performance in a single line of code.
But what happens if a is an lvalue? It seems there will be a copy
construction and then a move assignment (assuming A has a proper move
assignment operator). Move assignments can be costly if the object has
too many member variables.
Problem well spotted. I wouldn't go as far as to say that the pass-by-value-and-then-move construct is a bad idiom but it definitely has its potential pitfalls.
If your type is expensive to move and / or moving it is essentially just a copy, then the pass-by-value approach is suboptimal. Examples of such types would include types with a fixed size array as a member: It may be relatively expensive to move and a move is just a copy. See also
Small String Optimization and Move Operations and
"Want speed? Measure." (by Howard Hinnant)
in this context.
The pass-by-value approach has the advantage that you only need to maintain one function but you pay for this with performance. It depends on your application whether this maintenance advantage outweighs the loss in performance.
The pass by lvalue and rvalue reference approach can lead to maintenance headaches quickly if you have multiple arguments. Consider this:
#include <vector>
using namespace std;
struct A { vector<int> v; };
struct B { vector<int> v; };
struct C {
A a;
B b;
C(const A& a, const B& b) : a(a), b(b) { }
C(const A& a, B&& b) : a(a), b(move(b)) { }
C( A&& a, const B& b) : a(move(a)), b(b) { }
C( A&& a, B&& b) : a(move(a)), b(move(b)) { }
};
If you have multiple arguments, you will have a permutation problem. In this very simple example, it is probably still not that bad to maintain these 4 constructors. However, already in this simple case, I would seriously consider using the pass-by-value approach with a single function
C(A a, B b) : a(move(a)), b(move(b)) { }
instead of the above 4 constructors.
So long story short, neither approach is without drawbacks. Make your decisions based on actual profiling information, instead of optimizing prematurely.
The current answers are quite incomplete. Instead, I will try to conclude based on the lists of pros and cons I find.
Short answer
In short, it may be OK, but sometimes bad.
This idiom, namely the unifying interface, has better clarity (both in conceptual design and implementation) compared to forwarding templates or different overloads. It is sometimes used with copy-and-swap (actually, as well as move-and-swap in this case).
Detailed analysis
The pros are:
It needs only one function for each parameter list.
It needs indeed only one, not multiple ordinary overloads (or even 2n overloads when you have n parameters when each one can be unqualified or const-qualified).
Like within a forwarding template, parameters passed by value are compatible with not only const, but volatile, which reduce even more ordinary overloads.
Combined with the bullet above, you don't need 4n overloads to serve to {unqulified, const, const, const volatile} combinations for n parameters.
Compared to a forwarding template, it can be a non-templated function as long as the parameters are not needed to be generic (parameterized through template type parameters). This allows out-of-line definitions instead of template definitions needed to be instantiated for each instance in each translation unit, which can make significant improvement to translation-time performance (typically, during both compiling and linking).
It also makes other overloads (if any) easier to implement.
If you have a forwarding template for a parameter object type T, it may still clash with overloads having a parameter const T& in the same position, because the argument can be a lvalue of type T and the template instantiated with type T& (rather than const T&) for it can be more preferred by the overloading rule when there is no other way to differentiate which is the best overloading candidate. This inconsistency may be quite surprising.
In particular, consider you have forwarding template constructor with one parameter of type P&& in a class C. How many time will you forget to excluded the instance of P&& away from possibly cv-qualified C by SFINAE (e.g. by adding typename = enable_if_t<!is_same<C, decay_t<P>> to the template-parameter-list), to ensure it does not clash with copy/move constructors (even when the latter are explicitly user-provided)?
Since the parameter is passed by value of a non-reference type, it can force the argument be passed as a prvalue. This can make a difference when the argument is of a class literal type. Consider there is such a class with a static constexpr data member declared in some class without an out-of-class definition, when it is used as an argument to a parameter of lvalue reference type, it may eventually fail to link, because it is odr-used and there is no definition of it.
Note since ISO C++ 17 the rules of static constexpr data member have changed to introduce a definition implicitly, so the difference is not significant in this case.
The cons are:
A unifying interface can not replace copy and move constructors where the parameter object type is identical to the class. Otherwise, copy-initialization of the parameter would be infinite recursion, because it will call the unifying constructor, and the constructor then call itself.
As mentioned by other answers, if the cost of copy is not ignorable (cheap and predictable enough), this means you will almost always have the degeneration of performance in the calls when the copy is not needed, because copy-initialization of a unifying passed-by-value parameter unconditionally introduce a copy (either copied-to or moved-to) of the argument unless elided.
Even with mandatory elision since C++17, copy-initialization of a parameter object is still hardly free to be removed away - unless the implementation try very hard to prove the behavior not changed according to as-if rules instead of the dedicated copy elision rules applicable here, which might be sometimes impossible without a whole program analysis.
Likewise, the cost of destruction may not be ignorable as well, particularly when non-trivial subobjects are taken into account (e.g. in cases of containers). The difference is that, it does not only apply to the copy-initialization introduced by the copy construction, but also by the move construction. Making move cheaper than copy in constructors can not improve the situation. The more cost of copy-initialization, the more cost of destruction you have to afford.
A minor shortcoming is that there is no way to tweak the interface in different ways as plural overloads, for example, specifying different noexcept-specifiers for parameters of const& and && qualified types.
OTOH, in this example, unifying interface will usually provide you with noexcept(false) copy + noexcept move if you specifies noexcept, or always noexcept(false) when you specify nothing (or explicit noexcept(false)). (Note in the former case, noexcept does not prevent throwing during copy because that will only occur during evaluation of arguments, which is out of the function body.) There is no further chance to tune them separately.
This is considered minor because it is not frequently needed in reality.
Even if such overloads are used, they are probably confusing by nature: different specifiers may hide subtle but important behavioral differences which are difficult to reason about. Why not different names instead of overloads?
Note the example of noexcept may be particularly problematic since C++17 because noexcept-specification now affect the function type. (Some unexpected compatibility issues can be diagnosed by Clang++ warning.)
Sometimes the unconditional copy is actually useful. Because composition of operations with strong-exception guarantee does not hold the guarantee in nature, a copy can be used as a transactional state holder when the strong-exception guarantee is required and the operation cannot be broken down as sequence of operations with no less strict (no-exception or strong) exception guarantee. (This includes the copy-and-swap idiom, although assignments are not recommended to be unified for other reasons in general, see below.) However, this does not mean the copy is otherwise unacceptable. If the intention of the interface is always to create some object of type T, and the cost of moving T is ignorable, the copy can be moved to the target without unwanted overhead.
Conclusions
So for some given operations, here are suggestions about whether using a unifying interface to replace them:
If not all of the parameter types match the unifying interface, or if there is behavioral difference other than the cost of new copies among operations being unified, there cannot be a unifying interface.
If the following conditions are failed to be fit for all parameters, there cannot be a unifying interface. (But it can still be broken down to different named-functions, delegating one call to another.)
For any parameter of type T, if a copy of each argument is needed for all operations, use unifying.
If both copy and move construction of T have ignorable cost, use unifying.
If the intention of the interface is always to create some object of type T, and the cost of the move construction of T is ignorable, use unifying.
Otherwise, avoid unifying.
Here are some examples need to avoid unifying:
Assignment operations (including assignment to the subobjects thereof, typically with copy-and-swap idiom) for T without ignorable cost in copy and move constructions does not meet the criteria of unifying, because the intention of assignment is not to create (but to replace the content of) the object. The copied object will eventually be destructed, which incurs unnecessary overhead. This is even more obvious for cases of self-assignment.
Insertion of values to a container does not meet the criteria, unless both the copy-initialization and destruction have ignorable cost. If the operation fails (due to the allocation failure, duplicate values or so on) after copy-initialization, the parameters have to be destructed, which incurs unnecessary overhead.
Conditionally creation of object based on parameters will incur the overhead when it does not actually create the object (e.g. std::map::insert_or_assign-like container insertion even in spite of the failure above).
Note the accurate limit of "ignorable" cost is somewhat subjective because it eventually depends on how much cost can be tolerated by the developers and/or the users, and it may vary case by case.
Practically, I (conservatively) assume any trivially copyable and trivailly destructible type whose size is not more than one machine word (like a pointer) qualifying the criteria of ignorable cost in general - if the resulted code actually cost too much in such case, it suggests either a wrong configuration of the build tool is used, or the toolchain is not ready for production.
Do profile if there is any further doubt on performance.
Additional case study
There are some other well-known types preferred to be passed by value or not, depending on the conventions:
Types need to preserve reference values by convention should not be passed by value.
A canonical example is the argument forwarding call wrapper defined in ISO C++, which requires to forward references. Note in the caller position it may also preserve the reference respecting to the ref-qualifier.
An instance of this example is std::bind. See also the resolution of LWG 817.
Some generic code may directly copy some parameters. It may be even without std::move, because the cost of the copy is assumed to be ignorable and a move does not necessarily make it better.
Such parameters include iterators and function objects (except the case of argument forwarding caller wrappers discussed above).
Note the constructor template of std::function (but not the assignment operator template) also uses the pass-by-value functor parameter.
Types presumably having the cost comparable to pass-by-value parameter types with ignorable cost are also preferred to be pass-by-value. (Sometimes they are used as dedicated alternatives.) For example, instances of std::initializer_list and std::basic_string_view are more or less two pointers or a pointer plus a size. This fact makes them cheap enough to be directly passed without using references.
Some types should be better avoided passed by value unless you do need a copy. There are different reasons.
Avoid copy by default, because the copy may be quite expensive, or at least it is not easy to guarantee the copy is cheap without some inspection of the runtime properties of the value being copied. Containers are typical examples in this sort.
Without statically knowing how many elements in a container, it is generally not safe (in the sense of a DoS attack, for example) to be copied.
A nested container (of other containers) will easily make the performance problem of copying worse.
Even empty containers are not guaranteed cheap to be copied. (Strictly speaking, this depends on the concrete implementation of the container, e.g. the existence of the "sentinel" element for some node-based containers... But no, keep it simple, just avoid copying by default.)
Avoid copy by default, even when the performance is totally uninterested, because there can be some unexpected side effects.
In particular, allocator-awared containers and some other types with similar treatment to allocators ("container semantics", in David Krauss' word), should not be passed by value - allocator propagation is just another big semantic worm can.
A few other types conventionally depend. For example, see GotW #91 for shared_ptr instances. (However, not all smart pointers are like that; observer_ptr are more like raw pointers.)
For the general case where the value will be stored, the pass-by-value only is a good compromise-
For the case where you know that only lvalues will be passed (some tightly coupled code) it's unreasonable, unsmart.
For the case where one suspects a speed improvement by providing both, first THINK TWICE, and if that didn't help, MEASURE.
Where the value will not be stored I prefer the pass by reference, because that prevents umpteen needless copy operations.
Finally, if programming could be reduced to unthinking application of rules, we could leave it to robots. So IMHO it's not a good idea to focus so much on rules. Better to focus on what the advantages and costs are, for different situations. Costs include not only speed, but also e.g. code size and clarity. Rules can't generally handle such conflicts of interest.
Pass by value, then move is actually a good idiom for objects that you know are movable.
As you mentioned, if an rvalue is passed, it'll either elide the copy, or be moved, then within the constructor it will be moved.
You could overload the copy constructor and move constructor explicitly, however it gets more complicated if you have more than one parameter.
Consider the example,
class Obj {
public:
Obj(std::vector<int> x, std::vector<int> y)
: X(std::move(x)), Y(std::move(y)) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
Suppose if you wanted to provide explicit versions, you end up with 4 constructors like so:
class Obj {
public:
Obj(std::vector<int> &&x, std::vector<int> &&y)
: X(std::move(x)), Y(std::move(y)) {}
Obj(std::vector<int> &&x, const std::vector<int> &y)
: X(std::move(x)), Y(y) {}
Obj(const std::vector<int> &x, std::vector<int> &&y)
: X(x), Y(std::move(y)) {}
Obj(const std::vector<int> &x, const std::vector<int> &y)
: X(x), Y(y) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
As you can see, as you increase the number of parameters, the number of necessary constructors grow in permutations.
If you don't have a concrete type but have a templatized constructor, you can use perfect-forwarding like so:
class Obj {
public:
template <typename T, typename U>
Obj(T &&x, U &&y)
: X(std::forward<T>(x)), Y(std::forward<U>(y)) {}
private:
std::vector<int> X, Y;
}; // Obj
References:
Want Speed? Pass by Value
C++ Seasoning
I am answering myself because I will try to summarize some of the answers. How many moves/copies do we have in each case?
(A) Pass by value and move assignment construct, passing a X parameter. If X is a...
Temporary: 1 move (the copy is elided)
Lvalue: 1 copy 1 move
std::move(lvalue): 2 moves
(B) Pass by reference and copy assignment usual (pre C++11) construct. If X is a...
Temporary: 1 copy
Lvalue: 1 copy
std::move(lvalue): 1 copy
We can assume the three kinds of parameters are equally probable. So every 3 calls we have (A) 4 moves and 1 copy, or (B) 3 copies. I.e., in average, (A) 1.33 moves and 0.33 copies per call or (B) 1 copy per call.
If we come to a situation when our classes consist mostly of PODs, moves are as expensive as copies. So we would have 1.66 copies (or moves) per call to the setter in case (A) and 1 copies in case (B).
We can say that in some circumstances (PODs based types), the pass-by-value-and-then-move construct is a very bad idea. It is 66% slower and it depends on a C++11 feature.
On the other hand, if our classes include containers (which make use of dynamic memory), (A) should be much faster (except if we mostly pass lvalues).
Please, correct me if I'm wrong.
Readability in the declaration:
void foo1( A a ); // easy to read, but unless you see the implementation
// you don't know for sure if a std::move() is used.
void foo2( const A & a ); // longer declaration, but the interface shows
// that no copy is required on calling foo().
Performance:
A a;
foo1( a ); // copy + move
foo2( a ); // pass by reference + copy
Responsibilities:
A a;
foo1( a ); // caller copies, foo1 moves
foo2( a ); // foo2 copies
For typical inline code there is usually no difference when optimized.
But foo2() might do the copy only on certain conditions (e.g. insert into map if key does not exist), whereas for foo1() the copy will always be done.

About the implementations of c++ stl predicate

I wonder how c++ stl predicate is implemented? For example in copy_if()
http://www.cplusplus.com/reference/algorithm/copy_if/
According to Effective STL, predicate is passed by value. For the following code for int,
struct my_predicate{
int var_1;
float var_2;
bool operator()(const int& arg){
// some processing here
}
}
How is copy_if() implemented regarding to passing value of my_predicate? There are var_1 and var_2 here. For other predicates, there may be different variables in the struct.
If passing by reference or pointer, that is very reasonable to me.
Thanks a lot!
(I hope I'm not misunderstanding your question.)
The reason why it can be passed by value is that the 'my_predicate' struct has an implicit copy constructor automatically generated by the compiler. You can pass it by value because it has a copy constructor.
In practice, It is very likely the compiler will optimise away the copy. In fact it is very likely the compiler will optimise away the entire predicate object and for example in the case of std::copy_if reduce the code to the equivalent of a for loop + if statement.
By convention predicates are passed by value. They are not meant to be heavy weight objects and for small objects even if the entire predicate isn't optimised away, it is faster to pass by value anyway.
Also generally you cannot pass temporary values by non-const reference (let alone pointer) so:
std::copy_if(begin(..),end(..),my_predicate{});
would not compile as your predicate is not a const function. With pass by value you can get away with this.

Is the pass-by-value-and-then-move construct a bad idiom?

Since we have move semantics in C++, nowadays it is usual to do
void set_a(A a) { _a = std::move(a); }
The reasoning is that if a is an rvalue, the copy will be elided and there will be just one move.
But what happens if a is an lvalue? It seems there will be a copy construction and then a move assignment (assuming A has a proper move assignment operator). Move assignments can be costly if the object has too many member variables.
On the other hand, if we do
void set_a(const A& a) { _a = a; }
There will be just one copy assignment. Can we say this way is preferred over the pass-by-value idiom if we will pass lvalues?
Expensive-to-move types are rare in modern C++ usage. If you are concerned about the cost of the move, write both overloads:
void set_a(const A& a) { _a = a; }
void set_a(A&& a) { _a = std::move(a); }
or a perfect-forwarding setter:
template <typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
that will accept lvalues, rvalues, and anything else implicitly convertible to decltype(_a) without requiring extra copies or moves.
Despite requiring an extra move when setting from an lvalue, the idiom is not bad since (a) the vast majority of types provide constant-time moves and (b) copy-and-swap provides exception safety and near-optimal performance in a single line of code.
But what happens if a is an lvalue? It seems there will be a copy
construction and then a move assignment (assuming A has a proper move
assignment operator). Move assignments can be costly if the object has
too many member variables.
Problem well spotted. I wouldn't go as far as to say that the pass-by-value-and-then-move construct is a bad idiom but it definitely has its potential pitfalls.
If your type is expensive to move and / or moving it is essentially just a copy, then the pass-by-value approach is suboptimal. Examples of such types would include types with a fixed size array as a member: It may be relatively expensive to move and a move is just a copy. See also
Small String Optimization and Move Operations and
"Want speed? Measure." (by Howard Hinnant)
in this context.
The pass-by-value approach has the advantage that you only need to maintain one function but you pay for this with performance. It depends on your application whether this maintenance advantage outweighs the loss in performance.
The pass by lvalue and rvalue reference approach can lead to maintenance headaches quickly if you have multiple arguments. Consider this:
#include <vector>
using namespace std;
struct A { vector<int> v; };
struct B { vector<int> v; };
struct C {
A a;
B b;
C(const A& a, const B& b) : a(a), b(b) { }
C(const A& a, B&& b) : a(a), b(move(b)) { }
C( A&& a, const B& b) : a(move(a)), b(b) { }
C( A&& a, B&& b) : a(move(a)), b(move(b)) { }
};
If you have multiple arguments, you will have a permutation problem. In this very simple example, it is probably still not that bad to maintain these 4 constructors. However, already in this simple case, I would seriously consider using the pass-by-value approach with a single function
C(A a, B b) : a(move(a)), b(move(b)) { }
instead of the above 4 constructors.
So long story short, neither approach is without drawbacks. Make your decisions based on actual profiling information, instead of optimizing prematurely.
The current answers are quite incomplete. Instead, I will try to conclude based on the lists of pros and cons I find.
Short answer
In short, it may be OK, but sometimes bad.
This idiom, namely the unifying interface, has better clarity (both in conceptual design and implementation) compared to forwarding templates or different overloads. It is sometimes used with copy-and-swap (actually, as well as move-and-swap in this case).
Detailed analysis
The pros are:
It needs only one function for each parameter list.
It needs indeed only one, not multiple ordinary overloads (or even 2n overloads when you have n parameters when each one can be unqualified or const-qualified).
Like within a forwarding template, parameters passed by value are compatible with not only const, but volatile, which reduce even more ordinary overloads.
Combined with the bullet above, you don't need 4n overloads to serve to {unqulified, const, const, const volatile} combinations for n parameters.
Compared to a forwarding template, it can be a non-templated function as long as the parameters are not needed to be generic (parameterized through template type parameters). This allows out-of-line definitions instead of template definitions needed to be instantiated for each instance in each translation unit, which can make significant improvement to translation-time performance (typically, during both compiling and linking).
It also makes other overloads (if any) easier to implement.
If you have a forwarding template for a parameter object type T, it may still clash with overloads having a parameter const T& in the same position, because the argument can be a lvalue of type T and the template instantiated with type T& (rather than const T&) for it can be more preferred by the overloading rule when there is no other way to differentiate which is the best overloading candidate. This inconsistency may be quite surprising.
In particular, consider you have forwarding template constructor with one parameter of type P&& in a class C. How many time will you forget to excluded the instance of P&& away from possibly cv-qualified C by SFINAE (e.g. by adding typename = enable_if_t<!is_same<C, decay_t<P>> to the template-parameter-list), to ensure it does not clash with copy/move constructors (even when the latter are explicitly user-provided)?
Since the parameter is passed by value of a non-reference type, it can force the argument be passed as a prvalue. This can make a difference when the argument is of a class literal type. Consider there is such a class with a static constexpr data member declared in some class without an out-of-class definition, when it is used as an argument to a parameter of lvalue reference type, it may eventually fail to link, because it is odr-used and there is no definition of it.
Note since ISO C++ 17 the rules of static constexpr data member have changed to introduce a definition implicitly, so the difference is not significant in this case.
The cons are:
A unifying interface can not replace copy and move constructors where the parameter object type is identical to the class. Otherwise, copy-initialization of the parameter would be infinite recursion, because it will call the unifying constructor, and the constructor then call itself.
As mentioned by other answers, if the cost of copy is not ignorable (cheap and predictable enough), this means you will almost always have the degeneration of performance in the calls when the copy is not needed, because copy-initialization of a unifying passed-by-value parameter unconditionally introduce a copy (either copied-to or moved-to) of the argument unless elided.
Even with mandatory elision since C++17, copy-initialization of a parameter object is still hardly free to be removed away - unless the implementation try very hard to prove the behavior not changed according to as-if rules instead of the dedicated copy elision rules applicable here, which might be sometimes impossible without a whole program analysis.
Likewise, the cost of destruction may not be ignorable as well, particularly when non-trivial subobjects are taken into account (e.g. in cases of containers). The difference is that, it does not only apply to the copy-initialization introduced by the copy construction, but also by the move construction. Making move cheaper than copy in constructors can not improve the situation. The more cost of copy-initialization, the more cost of destruction you have to afford.
A minor shortcoming is that there is no way to tweak the interface in different ways as plural overloads, for example, specifying different noexcept-specifiers for parameters of const& and && qualified types.
OTOH, in this example, unifying interface will usually provide you with noexcept(false) copy + noexcept move if you specifies noexcept, or always noexcept(false) when you specify nothing (or explicit noexcept(false)). (Note in the former case, noexcept does not prevent throwing during copy because that will only occur during evaluation of arguments, which is out of the function body.) There is no further chance to tune them separately.
This is considered minor because it is not frequently needed in reality.
Even if such overloads are used, they are probably confusing by nature: different specifiers may hide subtle but important behavioral differences which are difficult to reason about. Why not different names instead of overloads?
Note the example of noexcept may be particularly problematic since C++17 because noexcept-specification now affect the function type. (Some unexpected compatibility issues can be diagnosed by Clang++ warning.)
Sometimes the unconditional copy is actually useful. Because composition of operations with strong-exception guarantee does not hold the guarantee in nature, a copy can be used as a transactional state holder when the strong-exception guarantee is required and the operation cannot be broken down as sequence of operations with no less strict (no-exception or strong) exception guarantee. (This includes the copy-and-swap idiom, although assignments are not recommended to be unified for other reasons in general, see below.) However, this does not mean the copy is otherwise unacceptable. If the intention of the interface is always to create some object of type T, and the cost of moving T is ignorable, the copy can be moved to the target without unwanted overhead.
Conclusions
So for some given operations, here are suggestions about whether using a unifying interface to replace them:
If not all of the parameter types match the unifying interface, or if there is behavioral difference other than the cost of new copies among operations being unified, there cannot be a unifying interface.
If the following conditions are failed to be fit for all parameters, there cannot be a unifying interface. (But it can still be broken down to different named-functions, delegating one call to another.)
For any parameter of type T, if a copy of each argument is needed for all operations, use unifying.
If both copy and move construction of T have ignorable cost, use unifying.
If the intention of the interface is always to create some object of type T, and the cost of the move construction of T is ignorable, use unifying.
Otherwise, avoid unifying.
Here are some examples need to avoid unifying:
Assignment operations (including assignment to the subobjects thereof, typically with copy-and-swap idiom) for T without ignorable cost in copy and move constructions does not meet the criteria of unifying, because the intention of assignment is not to create (but to replace the content of) the object. The copied object will eventually be destructed, which incurs unnecessary overhead. This is even more obvious for cases of self-assignment.
Insertion of values to a container does not meet the criteria, unless both the copy-initialization and destruction have ignorable cost. If the operation fails (due to the allocation failure, duplicate values or so on) after copy-initialization, the parameters have to be destructed, which incurs unnecessary overhead.
Conditionally creation of object based on parameters will incur the overhead when it does not actually create the object (e.g. std::map::insert_or_assign-like container insertion even in spite of the failure above).
Note the accurate limit of "ignorable" cost is somewhat subjective because it eventually depends on how much cost can be tolerated by the developers and/or the users, and it may vary case by case.
Practically, I (conservatively) assume any trivially copyable and trivailly destructible type whose size is not more than one machine word (like a pointer) qualifying the criteria of ignorable cost in general - if the resulted code actually cost too much in such case, it suggests either a wrong configuration of the build tool is used, or the toolchain is not ready for production.
Do profile if there is any further doubt on performance.
Additional case study
There are some other well-known types preferred to be passed by value or not, depending on the conventions:
Types need to preserve reference values by convention should not be passed by value.
A canonical example is the argument forwarding call wrapper defined in ISO C++, which requires to forward references. Note in the caller position it may also preserve the reference respecting to the ref-qualifier.
An instance of this example is std::bind. See also the resolution of LWG 817.
Some generic code may directly copy some parameters. It may be even without std::move, because the cost of the copy is assumed to be ignorable and a move does not necessarily make it better.
Such parameters include iterators and function objects (except the case of argument forwarding caller wrappers discussed above).
Note the constructor template of std::function (but not the assignment operator template) also uses the pass-by-value functor parameter.
Types presumably having the cost comparable to pass-by-value parameter types with ignorable cost are also preferred to be pass-by-value. (Sometimes they are used as dedicated alternatives.) For example, instances of std::initializer_list and std::basic_string_view are more or less two pointers or a pointer plus a size. This fact makes them cheap enough to be directly passed without using references.
Some types should be better avoided passed by value unless you do need a copy. There are different reasons.
Avoid copy by default, because the copy may be quite expensive, or at least it is not easy to guarantee the copy is cheap without some inspection of the runtime properties of the value being copied. Containers are typical examples in this sort.
Without statically knowing how many elements in a container, it is generally not safe (in the sense of a DoS attack, for example) to be copied.
A nested container (of other containers) will easily make the performance problem of copying worse.
Even empty containers are not guaranteed cheap to be copied. (Strictly speaking, this depends on the concrete implementation of the container, e.g. the existence of the "sentinel" element for some node-based containers... But no, keep it simple, just avoid copying by default.)
Avoid copy by default, even when the performance is totally uninterested, because there can be some unexpected side effects.
In particular, allocator-awared containers and some other types with similar treatment to allocators ("container semantics", in David Krauss' word), should not be passed by value - allocator propagation is just another big semantic worm can.
A few other types conventionally depend. For example, see GotW #91 for shared_ptr instances. (However, not all smart pointers are like that; observer_ptr are more like raw pointers.)
For the general case where the value will be stored, the pass-by-value only is a good compromise-
For the case where you know that only lvalues will be passed (some tightly coupled code) it's unreasonable, unsmart.
For the case where one suspects a speed improvement by providing both, first THINK TWICE, and if that didn't help, MEASURE.
Where the value will not be stored I prefer the pass by reference, because that prevents umpteen needless copy operations.
Finally, if programming could be reduced to unthinking application of rules, we could leave it to robots. So IMHO it's not a good idea to focus so much on rules. Better to focus on what the advantages and costs are, for different situations. Costs include not only speed, but also e.g. code size and clarity. Rules can't generally handle such conflicts of interest.
Pass by value, then move is actually a good idiom for objects that you know are movable.
As you mentioned, if an rvalue is passed, it'll either elide the copy, or be moved, then within the constructor it will be moved.
You could overload the copy constructor and move constructor explicitly, however it gets more complicated if you have more than one parameter.
Consider the example,
class Obj {
public:
Obj(std::vector<int> x, std::vector<int> y)
: X(std::move(x)), Y(std::move(y)) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
Suppose if you wanted to provide explicit versions, you end up with 4 constructors like so:
class Obj {
public:
Obj(std::vector<int> &&x, std::vector<int> &&y)
: X(std::move(x)), Y(std::move(y)) {}
Obj(std::vector<int> &&x, const std::vector<int> &y)
: X(std::move(x)), Y(y) {}
Obj(const std::vector<int> &x, std::vector<int> &&y)
: X(x), Y(std::move(y)) {}
Obj(const std::vector<int> &x, const std::vector<int> &y)
: X(x), Y(y) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
As you can see, as you increase the number of parameters, the number of necessary constructors grow in permutations.
If you don't have a concrete type but have a templatized constructor, you can use perfect-forwarding like so:
class Obj {
public:
template <typename T, typename U>
Obj(T &&x, U &&y)
: X(std::forward<T>(x)), Y(std::forward<U>(y)) {}
private:
std::vector<int> X, Y;
}; // Obj
References:
Want Speed? Pass by Value
C++ Seasoning
I am answering myself because I will try to summarize some of the answers. How many moves/copies do we have in each case?
(A) Pass by value and move assignment construct, passing a X parameter. If X is a...
Temporary: 1 move (the copy is elided)
Lvalue: 1 copy 1 move
std::move(lvalue): 2 moves
(B) Pass by reference and copy assignment usual (pre C++11) construct. If X is a...
Temporary: 1 copy
Lvalue: 1 copy
std::move(lvalue): 1 copy
We can assume the three kinds of parameters are equally probable. So every 3 calls we have (A) 4 moves and 1 copy, or (B) 3 copies. I.e., in average, (A) 1.33 moves and 0.33 copies per call or (B) 1 copy per call.
If we come to a situation when our classes consist mostly of PODs, moves are as expensive as copies. So we would have 1.66 copies (or moves) per call to the setter in case (A) and 1 copies in case (B).
We can say that in some circumstances (PODs based types), the pass-by-value-and-then-move construct is a very bad idea. It is 66% slower and it depends on a C++11 feature.
On the other hand, if our classes include containers (which make use of dynamic memory), (A) should be much faster (except if we mostly pass lvalues).
Please, correct me if I'm wrong.
Readability in the declaration:
void foo1( A a ); // easy to read, but unless you see the implementation
// you don't know for sure if a std::move() is used.
void foo2( const A & a ); // longer declaration, but the interface shows
// that no copy is required on calling foo().
Performance:
A a;
foo1( a ); // copy + move
foo2( a ); // pass by reference + copy
Responsibilities:
A a;
foo1( a ); // caller copies, foo1 moves
foo2( a ); // foo2 copies
For typical inline code there is usually no difference when optimized.
But foo2() might do the copy only on certain conditions (e.g. insert into map if key does not exist), whereas for foo1() the copy will always be done.

C++11 std::set lambda comparison function

I want to create a std::set with a custom comparison function. I could define it as a class with operator(), but I wanted to enjoy the ability to define a lambda where it is used, so I decided to define the lambda function in the initialization list of the constructor of the class which has the std::set as a member. But I can't get the type of the lambda. Before I proceed, here's an example:
class Foo
{
private:
std::set<int, /*???*/> numbers;
public:
Foo () : numbers ([](int x, int y)
{
return x < y;
})
{
}
};
I found two solutions after searching: one, using std::function. Just have the set comparison function type be std::function<bool (int, int)> and pass the lambda exactly like I did. The second solution is to write a make_set function, like std::make_pair.
SOLUTION 1:
class Foo
{
private:
std::set<int, std::function<bool (int, int)> numbers;
public:
Foo () : numbers ([](int x, int y)
{
return x < y;
})
{
}
};
SOLUTION 2:
template <class Key, class Compare>
std::set<Key, Compare> make_set (Compare compare)
{
return std::set<Key, Compare> (compare);
}
The question is, do I have a good reason to prefer one solution over the other? I prefer the first one because it makes use of standard features (make_set is not a standard function), but I wonder: does using std::function make the code (potentially) slower? I mean, does it lower the chance the compiler inlines the comparison function, or it should be smart enough to behave exactly the same like it would it was a lambda function type and not std::function (I know, in this case it can't be a lambda type, but you know, I'm asking in general) ?
(I use GCC, but I'd like to know what popular compilers do in general)
SUMMARY, AFTER I GOT LOTS OF GREAT ANSWERS:
If speed is critical, the best solution is to use an class with operator() aka functor. It's easiest for the compiler to optimize and avoid any indirections.
For easy maintenance and a better general-purpose solution, using C++11 features, use std::function. It's still fast (just a little bit slower than the functor, but it may be negligible) and you can use any function - std::function, lambda, any callable object.
There's also an option to use a function pointer, but if there's no speed issue I think std::function is better (if you use C++11).
There's an option to define the lambda function somewhere else, but then you gain nothing from the comparison function being a lambda expression, since you could as well make it a class with operator() and the location of definition wouldn't be the set construction anyway.
There are more ideas, such as using delegation. If you want a more thorough explanation of all solutions, read the answers :)
It's unlikely that the compiler will be able to inline a std::function call, whereas any compiler that supports lambdas would almost certainly inline the functor version, including if that functor is a lambda not hidden by a std::function.
You could use decltype to get the lambda's comparator type:
#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>
int main()
{
auto comp = [](int x, int y){ return x < y; };
auto set = std::set<int,decltype(comp)>( comp );
set.insert(1);
set.insert(10);
set.insert(1); // Dupe!
set.insert(2);
std::copy( set.begin(), set.end(), std::ostream_iterator<int>(std::cout, "\n") );
}
Which prints:
1
2
10
See it run live on Coliru.
Yes, a std::function introduces nearly unavoidable indirection to your set. While the compiler can always, in theory, figure out that all use of your set's std::function involves calling it on a lambda that is always the exact same lambda, that is both hard and extremely fragile.
Fragile, because before the compiler can prove to itself that all calls to that std::function are actually calls to your lambda, it must prove that no access to your std::set ever sets the std::function to anything but your lambda. Which means it has to track down all possible routes to reach your std::set in all compilation units and prove none of them do it.
This might be possible in some cases, but relatively innocuous changes could break it even if your compiler managed to prove it.
On the other hand, a functor with a stateless operator() has easy to prove behavior, and optimizations involving that are everyday things.
So yes, in practice I'd suspect std::function could be slower. On the other hand, std::function solution is easier to maintain than the make_set one, and exchanging programmer time for program performance is pretty fungible.
make_set has the serious disadvantage that any such set's type must be inferred from the call to make_set. Often a set stores persistent state, and not something you create on the stack then let fall out of scope.
If you created a static or global stateless lambda auto MyComp = [](A const&, A const&)->bool { ... }, you can use the std::set<A, decltype(MyComp)> syntax to create a set that can persist, yet is easy for the compiler to optimize (because all instances of decltype(MyComp) are stateless functors) and inline. I point this out, because you are sticking the set in a struct. (Or does your compiler support
struct Foo {
auto mySet = make_set<int>([](int l, int r){ return l<r; });
};
which I would find surprising!)
Finally, if you are worried about performance, consider that std::unordered_set is much faster (at the cost of being unable to iterate over the contents in order, and having to write/find a good hash), and that a sorted std::vector is better if you have a 2-phase "insert everything" then "query contents repeatedly". Simply stuff it into the vector first, then sort unique erase, then use the free equal_range algorithm.
A stateless lambda (i.e. one with no captures) can decay to a function pointer, so your type could be:
std::set<int, bool (*)(int, int)> numbers;
Otherwise I'd go for the make_set solution. If you won't use a one-line creation function because it's non-standard you're not going to get much code written!
From my experience playing around with the profiler, the best compromise between performance and beauty is to use a custom delegate implementation, such as:
https://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11
As the std::function is usually a bit too heavy. I can't comment on your specific circumstances, as I don't know them, though.
If you're determined to have the set as a class member, initializing its comparator at constructor time, then at least one level of indirection is unavoidable. Consider that as far as the compiler knows, you could add another constructor:
Foo () : numbers ([](int x, int y)
{
return x < y;
})
{
}
Foo (char) : numbers ([](int x, int y)
{
return x > y;
})
{
}
Once the you have an object of type Foo, the type of the set doesn't carry information on which constructor initialized its comparator, so to call the correct lambda requires an indirection to the run-time selected lambda operator().
Since you're using captureless lambdas, you could use the function pointer type bool (*)(int, int) as your comparator type, as captureless lambdas have the appropriate conversion function. This would of course involve an indirection through the function pointer.
The difference highly depends on your compiler's optimizations. If it optimizes lambda in a std::function those are equivalent, if not you introduce an indirection in the former that you won't have in the latter.