When I compile this program with either gcc-4.6.3 or gcc-4.7.2 the compiler gives me an error about the overloaded call being ambiguous:
#include <iostream>
#include <functional>
class Scott
{
public:
void func(const bool b = true)
{
std::cout << "Called func() with a boolean arg" << std::endl;
}
void func(std::function<void(void)> f)
#ifdef WITH_CONST
const
#endif
{
std::cout << "Called func() with a std::function arg" << std::endl;
}
};
int main (int argc, char *argv[])
{
Scott s;
s.func([] (void) { });
}
However, if I make the overloaded function const, it compiles fine & calls the method I did not expect!
devaus120>> g++ -Wall -std=c++11 -DWITH_CONST wtf.cxx
devaus120>> ./a.out
Called func() with a boolean arg
So, I have 2 questions:
TIA.
Scott. :)
Actually gcc is correct! Because lambda is not a function but a closure object of class type! Really! You can even inherit from it :) ... even multiple times from different lambdas...
So, according 8.5/16:
[...]
— If the destination type is a (possibly cv-qualified) class type:
[...]
— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
and 13.3.1.5:
Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion function can be invoked to convert an initializer expression of class type to the type of the object being initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:
-- The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2 X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions.
so finally, result of conversion function is a function pointer which would implicitly converted to bool
...
you may check this series of conversions with the following simple code:
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::boolalpha << []{ return 0; } << '\n';
}
the output will be true
...
Here is few ways to workaround... you definitely need something because both functions are suitable after overload resolution. Btw, adding const
, to signature of the second one, just exclude it because you've got a mutable instance of Scott
and again you'll get compile error if declare it w/ const
modifier.
So, you can do:
Func
. Depending on what you are going to do then, here is few options: it can be converted to std::function
on assign (if you want to store it to some member), or in case of immediate call you'll even get some optimization (by eliminate conversion to std::function
)std::enable_if
to turn one of them OFF depending on std::is_same<bool, T>
for example (or check for callable/function type)... I guess it's enough :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With