What is "->" after function declaration? - c++

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.

Related

What is this member function declaration? [duplicate]

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.

Can template type inference take primitive operation type conversions into account?

Templates don't seem to be able to type-infer the results of primitive operations. For example, the following fails to deduce R:
template<typename A, typename B, typename R>
R addNumbers(A x, B y){
return x + y;
}
main(){
addNumbers(1.0f, 1);
}
even though it's clear that float + int = float.
Is there some way, in any version of C++, to get the inference to take this information into account without explicitly specifying it?
Template parameter deduction when calling a function cannot look into the body of the function; it simply attempts to make the function's parameter types the same as the argument types, and any template parameters that cannot be deduced this way are left undeduced. You want a form of deduction that is based on the return statements in the body of the function. This is provided by auto in C++14:
template<typename A, typename B>
auto addNumbers(A x, B y){
return x + y;
}
decltype is one of the best additions in C++11.
Read more about it: http://en.cppreference.com/w/cpp/language/decltype
C++11 based solution (use compiler flag -std=c++11)
template<typename A, typename B>
auto addNumbers(A const & x, B const & y) -> decltype(x + y)
{
return x + y;
}
The best part of decltype, you can almost write any expression in it, and it will be evaluated at compile time.
In the above example, we are requesting the compiler to declare the return-type of the function, same as that of the return-type of the expression x + y, by just evaluating their types.
C++14 based solution (no need for decltype, use compiler flag -std=c++14)
template<typename A, typename B>
auto addNumbers(A const & x, B const & y)
{
return x + y;
}
Usage:
auto ans = addNumbers(1.0f, 1); // ans is deduced to be float
Function overload deduction, whether it considers templates or not, does not and cannot take return type into account. Return-type is not part of a function's signature. Deduction only considers whether the arguments of the call match parameters of the various function declarations. The following will match any A and B. If operator+ (A, B) is defined, the return type will be whatever it returns. If operator+ (A, B) is not defined, the compiler will throw an error.
template<typename A, typename B>
auto addNumbers(A x, B y){
return x + y;
}
int main(){
addNumbers(1.0f, 1);
}

Should my generic template use T or T&&?

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.

Why duplicate code is needed with const reference arguments?

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);

C++ ambiguous call in function overloading

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.