Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
325 views
in Technique[技术] by (71.8m points)

c++ - Resolving ambiguous overload on function pointer and std::function for a lambda using +

In the following code, the first call to foo is ambiguous, and therefore fails to compile.

The second, with the added + before the lambda, resolves to the function pointer overload.

#include <functional>

void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }

int main ()
{
    foo(  [](){} ); // ambiguous
    foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}

What is the + notation doing here?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The + in the expression +[](){} is the unary + operator. It is defined as follows in [expr.unary.op]/7:

The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument.

The lambda is not of arithmetic type etc., but it can be converted:

[expr.prim.lambda]/3

The type of the lambda-expression [...] is a unique, unnamed non-union class type — called the closure type — whose properties are described below.

[expr.prim.lambda]/6

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

Therefore, the unary + forces the conversion to the function pointer type, which is for this lambda void (*)(). Therefore, the type of the expression +[](){} is this function pointer type void (*)().

The second overload void foo(void (*f)()) becomes an Exact Match in the ranking for overload resolution and is therefore chosen unambiguously (as the first overload is NOT an Exact Match).


The lambda [](){} can be converted to std::function<void()> via the non-explicit template ctor of std::function, which takes any type that fulfils the Callable and CopyConstructible requirements.

The lambda can also be converted to void (*)() via the conversion function of the closure type (see above).

Both are user-defined conversion sequences, and of the same rank. That's why overload resolution fails in the first example due to ambiguity.


According to Cassio Neri, backed up by an argument by Daniel Krügler, this unary + trick should be specified behaviour, i.e. you can rely on it (see discussion in the comments).

Still, I'd recommend using an explicit cast to the function pointer type if you want to avoid the ambiguity: you don't need to ask on SO what is does and why it works ;)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...