I was playing around with C++ std::function objects, and wanted to add some functionality to them, so I subclassed them. However, when I tried to create a function that overloaded based on the signature of the function that was passed to it, the compiler told me that the call was ambiguous.
Can someone please help me to understand where the ambiguity lies, and also what I could do to disambiguate it?
Here's the sample code:
#include <functional>
#include <iostream>
class A0 : public std::function<void()>
{
public:
template <typename T>
A0(const T& t)
: std::function<void()> { t }
{
}
};
template <typename T1>
class A1 : public std::function<void(T1)>
{
public:
template <typename T>
A1(const T& t)
: std::function<void(T1)> { t }
{
}
};
void doThing(const A0& a0)
{
std::cout << "Do A0 thing\n";
}
void doThing(const A1<int>& a1)
{
std::cout << "Do A1 thing\n";
}
int main()
{
doThing([] (int i) {});
return 0;
}
And here's the compiler output:
g++ .\sources\Sample.cpp
.\sources\Sample.cpp: In function 'int main()':
.\sources\Sample.cpp:37:12: error: call of overloaded 'doThing(main()::<lambda(int)>)' is ambiguous
37 | doThing([] (int i) {});
| ~~~~~~~^~~~~~~~~~~~~~~
.\sources\Sample.cpp:25:6: note: candidate: 'void doThing(const A0&)'
25 | void doThing(const A0& a0)
| ^~~~~~~
.\sources\Sample.cpp:30:6: note: candidate: 'void doThing(const A1<int>&)'
30 | void doThing(const A1<int>& a1)
| ^~~~~~~
Both
A0andA1have a constructor that accepts any typeconst T&. The lambda expression[] (int i) {}could be used for either constructor, so neither wins in overload resolution.It doesn't really make sense that your constructor accepts any function object, because
std::functionalready supports this. Instead, accept the rightstd::functionin your constructor:See live example on Compiler Explorer
This solves the problem because constructing a
std::function<void()>from the lambda expression is impossible, so only the constructor ofA1participates in overload resolution. You can use option 1 or option 2, it's up to you. The choice depends on whether you want all of the constructors.Note: inheriting from standard library types is generally not a good idea. It's technically allowed, but they aren't meant to be used like this. See Extending the C++ Standard Library by inheritance?