Search code examples
c++templatesc++11decltype

How can I write a decltype expression using a function that expects a non-const reference?


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?


Solution

  • Use std::declval:

    template <typename A>
    auto doit(A& a) -> decltype(
        takes_int_ref(std::declval<
           decltype(convert_it(std::declval<A&>()))
           &>()), void())
    { .. }
    

    std::declval<A&>() gives you an expresion of type A&. convert_it(A&) will either be valid or not - if it's invalid, you fail there. If it's valid, say it has type T. Then, you try to call takes_int_ref with a T&, so to see if that's valid. If it is, you'll get to the void. If it's not, substitution failure.