如何使用 SFINAE 从 C++11 中的多个选项到 select 构造函数

How to use SFINAE to select constructor from multiple options in C++11

我的问题是这个问题的延伸:How to use sfinae for selecting constructors?

在上一个问题中,提问者只是想有选择地启用单个构造函数。我想根据 class 模板参数类型是否可默认构造来更改构造函数的行为——我能想到的最好的方法是让两个构造函数具有相同的用法,这样每个实例化都启用了一个。我的情况也不同,因为如果我不尝试使用 enable_if 有选择地启用,那么构造函数的两个版本都不是模板函数(而在链接的问题中,构造函数的两个版本都在 int otherN 上模板化).

上述问题的已接受答案中的评论让我想到了 this site,这让我创建了以下最小示例:

#include <iostream>
#include <type_traits>

namespace detail {
    enum class enabler {};
    enum class disabler {};
}

template <typename Condition>
using EnableIf = typename std::enable_if<Condition::value, detail::enabler>::type;

template <typename Condition>
using DisableIf = typename std::enable_if<!Condition::value, detail::disabler>::type;

template<typename T>
struct A {

    T data;

    // Valid if T is default-construtible; SFINAE otherwise
    template<EnableIf<std::is_default_constructible<T>>...>
    A() { std::cout << "Data defaulted" << std::endl; }


    // Valid if T is *not* default-constructible; SFINAE otherwise
    template<DisableIf<std::is_default_constructible<T>>...>
    A() : data(0) { std::cout << "Data zeroed" << std::endl; }
};

// struct which is not default-constructible
struct B {
    B() = delete;
    B(int) {}
};

int main()
{
    A<int> x; // int is default-constructible
    A<B> y; // class B is not default-constructible

    return 0;
}

如果我注释掉第一个构造函数和 x 的声明或第二个构造函数和 y 的声明,我可以编译它(使用 -std=c++11)。我什么都不想做,但是当我尝试编译器抱怨 std::enable_if<false, >.

中没有名为 type 的类型时

this 问题的答案采用了另一种方法来解决类似的问题,但我对起作用的因素了解不够深入,无法将这些方法组合成有效的方法。

不理想,但这可以完成工作:

#include <iostream>
#include <type_traits>

template<typename T>
struct A {

    T data;

    A() : A((std::is_default_constructible<T> *)nullptr) {}

private:
    A(std::true_type *) { std::cout << "Data defaulted" << std::endl; }

    A(std::false_type *) : data(0) { std::cout << "Data zeroed" << std::endl; }
};

// struct which is not default-constructible
struct B {
    B() = delete;
    B(int) {}
};

int main()
{
    A<int> x; // int is default-constructible
    A<B> y; // class B is not default-constructible

    return 0;
}

您还可以根据 T 及其默认可构造能力创建两个替代 class 实现:

#include <type_traits>
#include <iostream>

template <class T, class = void>
struct A_parent;

template <class T>
struct A_parent<T, typename std::enable_if<std::is_default_constructible<T>::value>::type> {
   T data;
   A_parent() {  std::cout << "Default constructable" << std::endl; }
};

template <class T>
struct A_parent<T, typename std::enable_if<!std::is_default_constructible<T>::value>::type> {
   T data;
   A_parent(): data(0) { std::cout << "Not default constructable" << std::endl; }
};

template <class T>
struct A: A_parent<T> {
   /* further implementation */
};

struct B {
    B() = delete;
    B(int) {}
};

int main() {
   A<int> a1;
   A<B> a2;
}

输出:

 Default constructable 
 Not default constructable

除了@Sam 的标签分发解决方案之外,您还可以在构造函数上使用 std::enable_if,但您必须注意以下几点:

  1. 用于std::is_default_constructible的模板参数不能是T,而是需要一个"new"模板参数(可以默认为T).有关详细信息,请参阅此 SO question/answer:std::enable_if to conditionally compile a member function

  2. 引用自cppreference.com

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

这导致以下代码:

#include <iostream>
#include <type_traits>

template<typename T>
struct A {

    T data;

    // Valid if T is default-constructible; SFINAE otherwise
    template<typename X = T, typename SFINAE = typename std::enable_if<std::is_default_constructible<X>::value>::type, typename P = SFINAE>
    A() { std::cout << "Data defaulted" << std::endl; }


    // Valid if T is *not* default-constructible; SFINAE otherwise
    template<typename X = T, typename = typename std::enable_if<!std::is_default_constructible<X>::value>::type>
    A() : data(0) { std::cout << "Data zeroed" << std::endl; }
};

// struct which is not default-constructible
struct B {
    B() = delete;
    B(int) {}
};

int main()
{
    A<int> x; // int is default-constructible
    A<B> y; // class B is not default-constructible

    return 0;
}

live example