【问题标题】:Select class constructor using enable_if使用 enable_if 选择类构造函数
【发布时间】:2013-07-24 10:17:26
【问题描述】:

考虑以下代码:

#include <iostream>
#include <type_traits>

template <typename T>
struct A {
    int val = 0;

    template <class = typename std::enable_if<T::value>::type>
    A(int n) : val(n) {};
    A(...) { }

    /* ... */
};

struct YES { constexpr static bool value = true; };
struct NO { constexpr static bool value = false; };

int main() {
    A<YES> y(10);
    A<NO> n;
    std::cout << "YES: " << y.val << std::endl
              << "NO:  " << n.val << std::endl;
}

我想选择性地为使用 enable_if 的某些类型定义构造函数 A::A(int)。对于所有其他类型,默认构造函数 A::A(...) 应该是替换失败时编译器的默认情况。然而这对我来说是有意义的编译器(gcc 版本 4.9.0 20130714)仍然在抱怨

sfinae.cpp:在“struct A”的实例化中:sfinae.cpp:19:11:
此处需要 sfinae.cpp:9:5: 错误:在
中没有名为“类型”的类型 '结构 std::enable_if'
A(int n) : val(n) {};

构造函数可以这样吗?这是否可以使用另一个构造函数(复制构造函数和移动构造函数)?

【问题讨论】:

  • 可能是个辅助工厂类?

标签: c++ templates constructor sfinae


【解决方案1】:

我认为这不适用于单个默认模板参数,因为在实例化类模板时需要解析其值。

我们需要将替换推迟到构造函数模板实例化点。一种方法是将模板参数默认为 T 并向构造函数添加一个额外的虚拟参数:

template<typename U = T>
A(int n, typename std::enable_if<U::value>::type* = 0) : val(n) { }

【讨论】:

  • 比我的解释更好:D
  • @JoelFalcou 你的确实有效。 This one doesn't still
  • @Rapptz 我的错误是因为它在默认模板参数中使用。它需要在构造函数的签名中。我会解决的。
  • @jrok 是more common to put it in the template parameters,但是你是对的,这对于单个模板参数是不可能的(这实际上是问题的答案)。
  • @bb94 我也是,所以我在 gcc 9.3.0 (mingw) 上进行了测试。没有 -O 它不会优化它。使用 -O1 它会删除它,并内联整个构造函数。 (基于检查反编译的 bin)。
【解决方案2】:

使用 C++20

您只需在模板中添加 requires 即可实现:

template <typename U = T> requires U::value
A(int n) : val(n) { }

requires 子句得到一个constant expression,其计算结果为truefalse,从而决定是否在重载决议中考虑此方法,如果requires 子句为真,或者否则忽略它。

代码:https://godbolt.org/z/CKTDFE

【讨论】:

  • 我选择这个作为答案,因为它似乎是最符合最新 C++ 标准的答案。对于需要向后兼容的人,我仍然推荐@jrok 的答案,因为它工作得很好。
【解决方案3】:

通常这是使用匿名默认参数完成的:

A(int n, typename std::enable_if<T::value>::type* = 0) : val(n) {};

你不能使用模板参数从类到 SFINAE out 方法。 所以一种方法是添加一个虚拟类型替换 int :

见:http://ideone.com/2Gnyzj

#include <iostream>
#include <type_traits>

template <typename T>
struct A {
    int val = 0;

    template<typename Integer
            ,typename  = typename std::enable_if<T::value && sizeof(Integer)>::type
            >
    A(Integer n) : val(n) {};

    A(...) {}
    /* ... */
};

struct YES { constexpr static bool value = true; };
struct NO { constexpr static bool value = false; };

int main() {
    A<YES> y(10);
    A<NO> n;
    std::cout << "YES: " << y.val << std::endl
              << "NO:  " << n.val << std::endl;
}

这是有效的,因为您使用成员模板参数将 SFINAE 排除在构造函数之外,但测试始终为真,因此它不会污染您的检查

【讨论】:

  • 这仍然给出了同样的错误,我的印象是 OP 的方法是最常见的。
  • @chris 确实如此。
  • 它在 C++11 中。我刚刚注意到 std::enable_if。
  • 这个解决方案非常有效。小缺陷是它改变了函数签名。有没有相同签名的解决方案?喜欢@jrok 的?
  • @tomas789 你可以使用另一个类型特征,as seen here
猜你喜欢
  • 2022-11-20
  • 1970-01-01
  • 2013-10-28
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多