Home > front end >  Why doesn't `auto &&` behave as a so-called "universal reference" when used as a lamb
Why doesn't `auto &&` behave as a so-called "universal reference" when used as a lamb

Time:03-05

Why does the && argument type in the following example not behave as a so-called "universal reference" even though it seems to be in a "deduced context", and instead is interpreted as an r-value:

auto main() -> int {    
    using MyFn = void (*)(int &);    
    MyFn special = [](auto && thing) {};  // compile error, deduced as int && rather than int&
    
    return 0;    
}

Whereas the following, which I expect to be equivalent, does work?

template <typename T> void foo(T &&){};

auto main() -> int {
    using MyFn = void (*)(int &);
    MyFn specialC = foo;
    return 0;
}

I suspect there's some esoteric detail with converting lambdas to function pointers that matters here, but so far I can't figure it out.

CodePudding user response:

Forwarding references are supported by a special deduction rule. The rule states that if we need to deduce T in order to make T&& identical to U&, then T is deduced as U& (so that T&& will be U& (&&), which just collapses down to U&).

However, this special rule only applies under certain circumstances: when deducing function template template arguments based on the arguments to a function call ([temp.deduct.call]/3) (in the case that the argument is an lvalue of type U, which you can imagine as having the type "U&"), when taking the address of a function template, and when matching a function template specialization declaration against a primary template declaration ([temp.deduct.type]/10).

Your case is none of the above, but is instead a separate case of deducing template arguments for a conversion function template ([temp.deduct.conv]), which doesn't have the special rule for forwarding references.

Another example where the forwarding reference deduction rules don't apply is the following, which will therefore not compile:

template <class T>
void foo(void(*)(T&&)) {}

void bar(int&) {}

int main() {
    foo(bar);
}

CodePudding user response:

In lambda parameters, auto by itself gives universal type deduction. So this does what you want:

MyFn special = [](auto thing) {};

Demo using a non-copyable type to prove it's not passing by value: https://godbolt.org/z/5vfWc6c8x

  • Related