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:
- Is it a compiler bug that this compiles when the overloaded method is made const?
- How can I ensure the correct overloaded function gets invoked? (Need to cast the argument somehow?)
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:
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 ofScottand again you’ll get compile error if declare it w/constmodifier.So, you can do:
Func. Depending on what you are going to do then, here is few options: it can be converted tostd::functionon 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 tostd::function)std::enable_ifto turn one of them OFF depending onstd::is_same<bool, T>for example (or check for callable/function type)… I guess it’s enough 🙂