使用enable_if选择类构造函数

tom*_*789 47 c++ templates constructor sfinae

考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

sfinae.cpp:在'struct A'的实例化中:sfinae.cpp:19:11:
从这里需要sfinae.cpp:9:5:错误:
'struct std :: enable_if'中没有名为'type'的类型
A(int n):val(n){};

这样的构造函数可能吗?这可能与另一个构造函数(复制构造函数和移动构造函数)?

jro*_*rok 38

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

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

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

  • @jrok [将它放在模板参数中更常见](http://coliru.stacked-crooked.com/view?id=a885f2da5f9f372004981a056f99d8f4-fcf98f666e0b687740619813293),这是不可能的2单个模板参数(这实际上是问题的答案)。 (4认同)
  • @bb94 我也是,所以我在 gcc 9.3.0 (mingw) 上进行了测试。如果没有 -O,它不会优化它。使用 -O1 它会删除它,并内联整个构造函数。(基于检查反编译的 bin)。 (2认同)

Joe*_*cou 11

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

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

您不能将类中的模板参数用于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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这仍然会产生同样的错误,我认为OP的方法是最常见的. (3认同)
  • @ tomas789你可以使用另一种类型特征,[见这里](http://ideone.com/fdo7JO) (3认同)

Ami*_*rsh 11

使用 C++20

您只需添加requires到模板即可实现这一点:

template <typename U = T> requires U::value
A(int n) : val(n) { }
Run Code Online (Sandbox Code Playgroud)

如果 requires 子句为真,则该requires子句将constant expression计算为truefalse决定是否在重载决议中考虑此方法,否则忽略它。

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

  • 我选择这个作为答案,因为它似乎是最符合最新 C++ 标准的答案。对于需要向后兼容的人,我仍然推荐 @jrok 的答案,因为它工作得很好。 (2认同)