I am working in a file that has a given function with many overloads, like so:
inline X f(ScriptWrappable* impl, Y y, Z z) { ... }
inline X f(Node* impl, Y y, Z z) { ... }
inline X f(RawPtr<T> impl, Y y, Z z) { ... }
inline X f(const RefPtr<T>& impl, Y y, Z z) { ... }
inline X f(ScriptWrappable* impl, Y y, Z z) { ... }
inline X f(const String& impl, Y y, Z z) { ... }
inline X f(int64_t impl, Y y, Z z) { ... }
template<typename T, size_t capacity> inline X f(const Vector<T, capacity>& impl, Y y, Z z) { ... }
I am trying to add a new overload that only requires one parameter, like so:
template<typename T> inline X f(T impl, W w) {
return f(impl, w->getY(), w->getZ());
}
I am using templates so that all of the above variations automatically work with my new two-parameter version.
However, during code review I was asked, "Is T&& better for avoiding copy?". That is, should I instead do
template<typename T> inline X f(T&& impl, W w) {
return f(impl, w->getY(), w->getZ());
}
I don't really know the answer to this question. I thought I understood universal references, but I am not familiar with when they are a good idea or not. What would be the consequences in my situation of choosing one over the other?
If I had to guess, I'd say that since the original types that T can stand in for are all simple to copy (primitives, references, or pointers) T&& will not give much benefit. But I'm still curious if e.g. any types could be passed to the T&& version that could not be passed to the T version, or vice versa.
You should write your function like this:
template<typename T> inline X f(T&& impl, W w) {
return f(std::forward<T>(impl), w->getY(), w->getZ());
}
This is called perfect forwarding. The type of impl will be exactly the same as if you had called f directly. It will not necessarily be an r-value reference.
The answer is a little complex because it requires some understanding of the compiler's rules regarding templates.
In the following declaration:
template<class T> void func(T&& t);
T is evaluated in deduced context and as a result will be treated by the compiler either as an r-value reference or a l-value reference, whichever is appropriate. Coupled with the use of std::forward (note: in this case not std::move) this results in perfect forwarding and is optimally efficient.
However, this is not being evaluated in deduced context:
template<class T>
struct X {
void foo(T&& t);
};
In this case, foo is in fact demanding an r-value reference.
Neither is this deduced context:
template<class T>
void foo(std::vector<T>&& v);
In these two cases, you should use std::move.
Related
In one system header file, I see the expression like this:
auto create_task(_Ty _Param) -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>
{…}
I don't know what "->" means, it isn't pointer expression or lambda expression, can anyone help me?
It's the new function declaration syntax from C++11, and it's called the "trailing return type". At the end of a function declaration, -> means that the following is the return type of the function. It can only be used when the auto keyword is used instead of an actual return type where you would normally expect it.
For instance, these two declarations are compatible:
int foo();
auto foo() -> int;
Depending on your tastes, you may find it prettier than the old declaration syntax, especially when the return type is extremely long/complex:
task<typename details::_TaskTypeFromParam<_Ty>::_Type> create_task(_Ty _Param);
auto create_task(_Ty _Param) -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>;
But sometimes it can be necessary with templates, when the return type of the function could vary with the arguments.
Say you want a templated function to add variables:
template<typename T>
T add(const T& x, const T& y)
{
return x + y;
}
That's great, but you'll only be able to add variables of the same type. Suppose you would like to be able to add variables of any type (like add((int)1, (double)2)).
template<typename T, typename U>
??? add(const T& x, const U& y)
{
return x + y;
}
EDIT: note that in C++14 and onwards, it's legal to write auto add(const T& x, const U& y), without a trailing return type, for function definitions (in other words, when you define the body of your function).
The problem is that you can't tell in advance what the result type of x + y will be. As templates stand, they could even be non-integral types. (Wouldn't you like to be able to do add(std::string("x"), "y")?)
Decltype, along with the new function declaration syntax, lets you solve this problem.
template<typename T, typename U>
auto add(const T& x, const U& y) -> decltype(x + y)
{
return x + y;
}
Decltype "returns" the type of an expression. Since you need x and y to have been declared for decltype(x + y) to work, you need the new syntax.
In this interview Stepanov shows how to implement generic max function in C++.
Try to implement a simple thing in the object oriented way, say, max.
I do not know how it can be done. Using generic programming I can
write:
template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x,
StrictWeakOrdered& y) {
return x < y ? y : x;
}
and
template <class StrictWeakOrdered>
inline const StrictWeakOrdered& max(const StrictWeakOrdered& x,
const StrictWeakOrdered& y) {
return x < y ? y : x;
}
(you do need both & and const &).
Why is there need to write the code twice? Is this needed to aid compiler for optimization or a convention to reduce bugs? Is max a special case where body of a const version is identical?
How many valid const and non-const permutations a function of N arguments should have to define a complete API?
First of all, you need the non-const version to allow stuff like
max(a, b) = something;
If you don't want to do such things, you can just provide the const version only to cover all cases. That is basically what the standard std::max does.
You also do not need to provide any more permutations of const and non-const, returning non-const& only makes sense if all inputs are non-const, all other cases are properly handled by the const version.
If you want to avoid code duplication, you can do something like this:
template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x, StrictWeakOrdered& y) {
const auto &xr = x;
const auto &yr = y;
return const_cast<StrictWeakOrdered&>(max(xr, yr));
}
In this special case, the const_cast is safe because you already know that the input is really non-const. Now you only have to provide the implementation for the const case.
So providing the implementation twice is not required and should not help the compiler, but whether or not the above is more readable than what Stepanov did is debatable.
You actually don't need both versions. You can write it this way.
template <class S, class T>
decltype(auto) max(S&& a, T&& b) {
using Ret = std::conditional_t<
std::is_const<std::remove_reference_t<S>>::value, S, T>;
if (b < a)
return std::forward<Ret>(a);
return std::forward<Ret>(b);
}
Falling back to const if either of the arguments was const.
If you do not intend to modify the argument, you can just go with the const& version. Everything should bind to a const reference.
C++11 also introduced reference collapsing, and a template parameter T&& is sometimes called a universal reference. In this case, when instantiating the parameter type for e.g. a int&, we would have int& && which collapses to int&. Now, you can write the function as
template <class T1, class T2>
inline T1 const& max(T1&& x, T2&& y) {
T1 const& x_=x;
T2 const& y_=y;
return (x_ < y_) ? (y_) : (x_);
}
This can be called with const values, temporaries (r-values) and mutable variables:
int const a=1;
int b=2;
max(b,b) = 23;
std::cout << max(a,a) << max( int{4}, int{5} ) << b << max(int{4}, a);
Assume you are providing a client library with a function that has multiple reference arguments.
For the sake of simplicity lets assume we have 3 arguments and those are various references to int (so you can assume, that these constants would also be created by initializer_list).
From a call site it should be possible to pass ad hoc created constants (rvalue) to functions (think of testing code in this case) as well as real references to objects being owned by another entity.
So far I've come up with the following solutions:
void bar(int &x, int &y, int &z) { }
// use a template wrapper to turn rvalues into lvalues.
template <typename T, typename U, typename V>
void foo(T &&t, U &&u, V &&v) {
bar(t,u,v);
}
// create a humongous amount of overloads
void baz(int &&x, int &y, int &z) { }
void baz(int &x, int &&y, int &z) { }
void baz(int &&x, int &&y, int &z) { }
void baz(int &x, int &y, int &&z) { }
void baz(int &&x, int &y, int &&z) { }
void baz(int &x, int &&y, int &&z) { }
void baz(int &&x, int &&y, int &&z) { }
// claim ownership of the objects and create temporaries
void bam(int x, int y, int z) { }
int main() {
int i = 1;
foo(i,2,3);
foo(2,i,3);
foo(2,3,i);
bar(i,2,3); // doesn't compile
bar(2,i,3);
bar(2,3,i);
baz(i,2,3); // requires 8 overloads
baz(2,i,3);
baz(2,3,i);
return 0;
}
I'm not completely satisfied with all of the solutions as every one of them has drawbacks. Is there a cleaner alternative to this problem?
This question is actually not trivial but there some guidelines that have evolved over the years and have actually not changed too much with C++11. For the following we will assume that you have a side-effect free, free-standing function (guidelines are slightly different for constructors and certain member functions).
What I would recommend for a free-standing function is:
Pass primitive data types (int, double, bool, etc.) by value
Pass complex data types by const reference (e.g. const std::vector&) unless you need a copy within the function then pass by value
If your function is templated use const reference (e.g. const T&) since both lvalue and rvalue will bind to const references
If your function is templated and you intend to perfect forward then use forwarding references (e.g. T&&, also called universal references)
So for your example using integers I would use the bam function:
void bam(int x, int y, int z) { }
There is an interesting talk by Herb Sutter given at the CppCon last year that
among other stuff covers input arguments to functions:
https://www.youtube.com/watch?v=xnqTKD8uD64
Another interesting post on stackoverflow related to this question:
Correct usage of rvalue references as parameters
Update for constructors:
The main difference for constructors to the recommendations above is a stronger emphasis on copying input and making the object owned by the class. The same holds for setters. The main reason is safer resource management (to avoid data races or accessing invalid memory).
This is the perfect forwarding problem. The way you avoid the 8 overloads of baz is to use the so-called universal references:
template <typename T, typename U, typename V>
void baz(T &&t, U &&u, V &&v) { }
In the above, t, u and v faithfully replicate the r/l-valueness for the parameters. For example, say your original intended function was this:
void baz(vector<int> &t, vector<int> &u, vector<int> &v) { }
and the other 7 overloads. With the templated version, if you call baz(a,b,c) where a and b are rvalues, while c isn't, T and U will be deduced to be vector<int>&& while V will be deduced to be vector<int>&. Thus, you have all the information you need in this implementation.
template<class T>
struct lrvalue {
T&t;
lrvalue(T&in):t(in){}
lrvalue(T&&in):t(in){}
};
take lrvalue<int>. Problem solved? You can dress them up, maybe inherit from reference_wrapper or somesuch instead of T&t.
You can also cast when calling:
template<class T>
T& lvalue(T&&t){return t;}
template<class T>
T& lvalue(T&t)=delete;//optional
now if you want to pass foo{} to foo& you can. lvalue(foo{}).
In one system header file, I see the expression like this:
auto create_task(_Ty _Param) -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>
{…}
I don't know what "->" means, it isn't pointer expression or lambda expression, can anyone help me?
It's the new function declaration syntax from C++11, and it's called the "trailing return type". At the end of a function declaration, -> means that the following is the return type of the function. It can only be used when the auto keyword is used instead of an actual return type where you would normally expect it.
For instance, these two declarations are compatible:
int foo();
auto foo() -> int;
Depending on your tastes, you may find it prettier than the old declaration syntax, especially when the return type is extremely long/complex:
task<typename details::_TaskTypeFromParam<_Ty>::_Type> create_task(_Ty _Param);
auto create_task(_Ty _Param) -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>;
But sometimes it can be necessary with templates, when the return type of the function could vary with the arguments.
Say you want a templated function to add variables:
template<typename T>
T add(const T& x, const T& y)
{
return x + y;
}
That's great, but you'll only be able to add variables of the same type. Suppose you would like to be able to add variables of any type (like add((int)1, (double)2)).
template<typename T, typename U>
??? add(const T& x, const U& y)
{
return x + y;
}
EDIT: note that in C++14 and onwards, it's legal to write auto add(const T& x, const U& y), without a trailing return type, for function definitions (in other words, when you define the body of your function).
The problem is that you can't tell in advance what the result type of x + y will be. As templates stand, they could even be non-integral types. (Wouldn't you like to be able to do add(std::string("x"), "y")?)
Decltype, along with the new function declaration syntax, lets you solve this problem.
template<typename T, typename U>
auto add(const T& x, const U& y) -> decltype(x + y)
{
return x + y;
}
Decltype "returns" the type of an expression. Since you need x and y to have been declared for decltype(x + y) to work, you need the new syntax.
Just a curiosity (academic question :P), consider the following code:
struct Y {};
class PassConstY {
public:
PassConstY(const Y& y) {}
};
class PassY {
public:
PassY(Y& y) {}
};
void z(PassConstY y) {}
void z(PassY y) {}
void h(const Y& y) {}
void h(Y& y) {}
Y y;
const Y cy;
h(cy); //OK
h(y); //OK
z(cy); //OK
z(y); //Ambiguity
Question: is it possible to write PassY and PassConstY s.t.
z and h obeys exactly the same overloading rules
if I remove the z and h definitions for the mutable Y the code still compiles (i.e. I can call z and h with the mutable version too).
My guess is no, in the sense that I managed to have PassConstY constructable from a const Y only (and not a mutable Y), which removes the ambiguity, but then point 2 is doomed to failure.
Clarifications:
PassY and PassConstY can be defined as you prefer (but must be classes, may be templated) but the definitions of z and h must be unchanged.
The following code must compile when only the "const" versions of z and h are defined:
const Y y;
z(y); //Calls z(PassConstY y)
h(y); //Calls h(const Y& y)
Y x;
z(x); //Calls z(PassConstY y)
h(x); //Calls h(const Y& y)
The following code must compile when only the "mutable" versions of z and h are defined:
Y x;
z(x); //Calls z(PassY y)
h(x); //Calls h(Y& y)
And the following code must compile when both versions of h and z are defined:
const Y y;
z(y); //Calls z(PassConstY y)
h(y); //Calls h(const Y& y)
Y x;
z(x); //Calls z(PassY y)
h(x); //Calls h(Y& y)
Sorry if the original question was not clear enough!
Motivations (really another question of mine here on Stackoverflow):
Regaring the "disable r-value binding" in the comment below, I would like to come up with a template class Ref (think of it like a smart reference) that I can use when I write function definitions like:
struct X {};
void f(Ref<X> x) {} //Instead of void f(X& x)
void f(Ref<const X> x) {} //Instead of void f(const X& x)
so that I get the same behaviour when calling the function f (in terms of overloading resolution/conversions) as for the plain reference versions, but Ref never binds to rvalues.
[For this part, C++0X is needed]
The reason of this is:
A SCS 1 is better than a SCS 2 if, among other things, SCS 1 binds a reference to const, and SCS 2 binds a reference to non-const.
A UCS 1 is better than a UCS 2 if and only if, among other things, both use the same conversion function or constructor.
For h, which uses a SCS, the first bullet will figure out a winner for the case where both functions are viable candidates (second call). For z, the case where both functions are viable candidates (second call) there is no UCS better than the other UCS, because both UCSs use different constructors.
Notice that an UCS is defined as
One SCS (converting the argument to the parameter type of the constructor in case a constructor is used, and to TypeOf(*this) & for a conversion operator function)
The call to the user defined conversion (constructor or conversion operator function)
Another SCS (converts the result of the conversion to the final destination type).
You cannot formate a SCS for "const Y to Y&", so there exist no first SCS for the UCS for the second h function, and so it is not a viable candidate. This is not true for the second call to h though, and it thus results in the ambiguity described above.
I've shortened some terms:
UCS: User-defined Conversion Sequence
SCS: Standard Conversion Sequence
My guess is no, in the sense that I managed to have PassConstY constructable from a const Y only (and not a mutable Y), which removes the ambiguity, but then point 2 is doomed to failure.
That suspicion of yours, if I understand you correctly, is not true. With your current definitions, you can remove the mutable versions of both h and z and your code will compile fine. There is an SCS for Y to const Y&.
You can, of course, rewrite PassY and PassConstY as
typedef Y &PassY;
typedef Y const& PassConstY;
Other than that, I'm not sure what you are heading at when you say "rewrite". If you want to just change the class body, then definitely overload resolution will be different.
I’m not sure if this may help you but you could write wrapper for z that uses templates:
template <typename T>
struct TypeFor {
typedef PassY type;
};
template <typename T>
struct TypeFor<const T> {
typedef PassConstY type;
};
template <typename T>
void wrap_z(T& y) { z(static_cast<typename TypeFor<T>::type>(y)); }
Now depending on your calling argument type, T will become either Y or const Y and the correct explicit conversion will be used.
This uses the fact that structs (but not functions) can be partially specialized, which we have done here for different constness of T.
z(y) is ambiguous because there is an implicit conversion in between and it does not know which one.
If you used explicit constructors to PassY and PassConstY then both z's would fail.
You could also make Pass a template with an implicit constructor:
template< typename T >
class Pass
{
Pass( T& t ) {}
};
typedef Pass<Y> PassY;
typedef Pass<const Y> PassConstY;
I am still not sure this would solve your problem though but you could also make z a template function:
template< typename T> void z( Pass<T> t );
although in real code I would avoid the implicit conversion and use a function
template<typename T> Pass<T> pass(T& t) { return Pass<T>(t); }
Now above is a partial-specialisation and you can also define
template< typename T> void z( T& t) { z( pass(t) ); }
and it should work.