Consider:
int convert_it(std::string& x)
{
return 5;
}
void takes_int_ref(int& i)
{
}
I want to write a function which only exists if convert_it
can be applied and the result passed into takes_int_ref
. That is, the function body is:
template <typename A>
void doit(A& a)
{
int i = convert_it(a);
takes_int_ref(i);
}
However, if I do:
template <typename A>
auto doit(A& a) -> decltype(takes_int_ref(convert_it(a)), void())
it doesn't work because invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
.
I thought of the following solution, which works:
template <typename T>
T& gimme_ref(T t) { throw std::runtime_error("No"); return t; }
template <typename A>
auto doit(A& a) -> decltype(takes_int_ref(gimme_ref(convert_it(a))), void())
However, it seems hackish and the decltype
no longer reflects what the function body does. In essence the problem seems to be that decltype
only takes an expression, while two statements are required in the function body here.
What would be the right approach to take here?
Use
std::declval
:std::declval<A&>()
gives you an expresion of typeA&
.convert_it(A&)
will either be valid or not - if it's invalid, you fail there. If it's valid, say it has typeT
. Then, you try to calltakes_int_ref
with aT&
, so to see if that's valid. If it is, you'll get to thevoid
. If it's not, substitution failure.