This answer has a code snippet like this :
template<class T, class F>
auto f(std::vector<T> v, F fun)
-> decltype( bool( fun(v[0] ) ), void() )
{
// ...
}
It really compiles and work (at least on Ideone).
So, how is the type deduced in this case?
Is next line really allowed by c++11 standard?
decltype( bool( fun(v[0] ) ), void() )
I took a quick look, and it doesn't look valid. Is ideone wrong in this case?
All examples in the c++11 standard are such that they all got only one type in the decltype :
struct A {
char g();
template<class T> auto f(T t) -> decltype(t + g())
{ return t + g(); }
};
another example :
void f3() {
float x, &r = x;
[=] {
decltype(x) y1;
decltype((x)) y2 = y1;
decltype(r) r1 = y1;
decltype((r)) r2 = y2;
};
and another
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = i;
decltype(i) x2;
decltype(a->x) x3;
decltype((a->x)) x4 = x3;
They all got only one parameter in decltype. How come the top code take two parameters (separated by a comma)?
I created another example (which fails to compile) :
#include <vector>
#include <iostream>
template<class T, class F>
auto f(std::vector<T> v, F fun) -> decltype(bool(fun(v[0])), void())
{
// ...
(void)v;(void)fun;
return fun(v.size());
}
void ops(int)
{
}
int main(){
std::vector<int> v;
f(v, [](int){ return true; });
f(v,ops);
}
Even if the line f(v,ops);
is removed, the return type of the f
template function is evaluated to void.
decltype( bool( fun(v[0] ) ), void() )
uses the comma operator.
Breaking it down,
bool( fun(v[0] ) ), void()
is composed of two expressions; the first
bool( fun(v[0] ) )
is evaluated1 and discarded, giving the overall expression the value
void()
which is a value2 of type void
.
decltype
then yields the type of the expression, which as above is void
.
The reason to use the comma operator here is to ensure that the whole expression is only valid if the first subexpression is valid; this is because it is being used in SFINAE to exclude it from substitution consideration if the first subexpression is invalid.
This works because although decltype
looks syntactically like a function, it is actually a language construct that (like sizeof
) is defined to take a single argument. It might be clearer to parenthesise the comma-operator argument:
decltype( ( bool( fun(v[0] ) ), void() ) )
bool( fun(v[0] ) )
is not actually evaluated, because we're in a non-evaluated context (decltype
, similar to sizeof
). What matters here is that it would be evaluated if the expression as a whole was evaluated, so that if the subexpression is invalid then the whole expression is invalid.void()
isn't really a value, but it behaves like a value in the context of the comma operator and decltype
.