【问题标题】:SFINAE Duplicate constructor declarationSFINAE 重复构造函数声明
【发布时间】:2015-07-02 01:16:42
【问题描述】:

我想为一个类创建构造函数,以便编译器在需要时轻松创建它的新实例。

这是一个例子。

class C {
    public:
    C(int) {}; // int constructor
};

如果我再声明一个函数:

void F(C _c) {};

我可以用 int 调用它,并让编译器处理 C 的构造:

F(0); // works

我想做的是实现同样的事情,但是用lambdas作为参数,举几个例子:

F([]() {});                     //A
F([](int) {});                  //B
F([](int)->int { return 0; });  //C

通过 SFINAE 以及我从另一个问题中学到的知识:Auto-constructor not working with <functional> objects

我设法找到了一种方法来创建一个只匹配特定 lambda 签名的构造函数,它的工作原理如下:

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

现在我遇到的问题是,如果我一次添加这三个定义,我会收到一条错误消息,指出我正在尝试重新定义 C 的构造函数。这是正确的,因为,是的,构造函数被定义为 C (F&& f) 三次,但是,我应该如何让编译器知道对每种不同的情况使用不同的签名?

另一个答案提示我查看 enable_if 和 is_convertible 但尚未设法为此问题设置解决方法。非常感谢任何帮助。

使用:Apple LLVM 6.0 版 (clang-600.0.57)(基于 LLVM 3.5svn)

【问题讨论】:

标签: c++ constructor sfinae


【解决方案1】:

你当前的问题是你定义了 3 次

template <typename F, typename> C(F&&);

但使用不同的默认参数(执行 SFINAE)。

你可以改成

// 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);

所以你有 3 个不同的签名:

template<typename F, dummy<0>*> C(F&& f);
template<typename F, dummy<1>*> C(F&& f);
template<typename F, dummy<2>*> C(F&& f);

【讨论】:

  • 谢谢@Jarod42,为什么当我省略“= nullptr”时它会失败?
  • @almosnow:如果你省略了= nullptr,你必须提供一个有效的参数(dummy&lt;N&gt;*
【解决方案2】:

基于top answer here,这是我想出的。它可能有点粗糙,可以改进,但主要是它的工作原理(在在线 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([]() {});
}

【讨论】:

  • 谢谢亚历克斯,为了简单起见,我接受了 Jarod 的回答,但你的回答也很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-04
  • 2016-08-11
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 2019-09-18
相关资源
最近更新 更多