问题
I want to create constructors for a class in a way that the compiler trivially create new instances of it when needed.
Here's an example.
class C {
public:
C(int) {}; // int constructor
};
If I then declare a function:
void F(C _c) {};
I can call this one with an int and have the construction of C handled by the compiler:
F(0); // works
What I want to do is to achieve the same thing, but with lambdas as parameters, a few examples:
F([]() {}); //A
F([](int) {}); //B
F([](int)->int { return 0; }); //C
With SFINAE and from what I've learned from another question: Auto-constructor not working with <functional> objects
I managed to sort out a way to create a constructor that matches only a specific lambda signature, it would work out like this:
template<typename F, typename = decltype(function<void(void)>(declval<F&>()))> C(F&& f) {}; //For lambda's like A
template<typename F, typename = decltype(function<void(int)>(declval<F&>()))> C(F&& f) {}; //For lamdba's like B
template<typename F, typename = decltype(function<int(int)>(declval<F&>()))> C(F&& f) {}; //For lambda's like C
Now the problem that I have is that if I add these three definitions at once, I get an error stating that I'm trying to redefine the constructor of C. This is correct because, yeah, the constructor's being defined as C(F&& f) three times, however, how should I let know the compiler to use a different signature for each different case?
The other answer hinted me to look at enable_if and is_convertible but haven't managed to set up a workaround for this issue. Any help is greatly appreciated.
Using: Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
回答1:
Your current problem is that you define 3 times
template <typename F, typename> C(F&&);
but with different default argument (to do SFINAE).
You may change to
// Use to have different type easily
template <std::size_t> struct dummy {}
template<typename F, decltype(function<void(void)>(declval<F&>()), dummy<0>())* = nullptr> C(F&& f);
template<typename F, decltype(function<void(int)>(declval<F&>()), dummy<1>())* = nullptr> C(F&& f);
template<typename F, decltype(function<int(int)>(declval<F&>()), dummy<2>())* = nullptr> C(F&& f);
So you have 3 different signatures:
template<typename F, dummy<0>*> C(F&& f);
template<typename F, dummy<1>*> C(F&& f);
template<typename F, dummy<2>*> C(F&& f);
回答2:
Based on top answer here, this is what I came up with. It is probably a little crude and can be improved on but the main thing it works (tried on online clang 3.5.0).
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
typedef ReturnType(signature)(Args... args);
typedef ReturnType(*ptr_signature)(Args... args);
};
class C
{
public:
template<typename F> C(F&& f) {
typedef function_traits<F> traits;
I<F>(std::move(f), reinterpret_cast<typename traits::ptr_signature>(0));
}
template<typename F> void I(F&& f, void(*)()) { std::cout << "Hello void(void) lambda" << std::endl; };
template<typename F> void I(F&& f, void(*)(int)) { std::cout << "Hello void(int) lambda" << std::endl; };
template<typename F> void I(F&& f, int(*)(int)) { std::cout << "Hello int(int) lambda" << std::endl; };
};
int main()
{
C([](int i) { return i;});
C([](int i) {});
C([]() {});
}
来源:https://stackoverflow.com/questions/31152476/sfinae-duplicate-constructor-declaration