【问题标题】:How to use SFINAE to select constructor from multiple options in C++11如何使用 SFINAE 从 C++11 中的多个选项中选择构造函数
【发布时间】:2016-07-23 01:04:24
【问题描述】:

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

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

上述问题的已接受答案中的 cmets 将我引导至 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&lt;false, &gt; 中没有名为type 的类型时。

this 问题的答案对类似问题采取了另一种方法,但我不太了解起作用的因素,无法将这些方法组合成可行的方法。

【问题讨论】:

标签: c++ templates c++11 sfinae


【解决方案1】:

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

#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;
}

【讨论】:

  • 删除指针并传递std::is_default_constructible&lt;T&gt;::type 反而更容易阅读imo ...否则+1。
【解决方案2】:

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

  1. 用于std::is_default_constructible 的模板参数不能是T,而是需要一个“新”模板参数(可以默认为T)。有关详细信息,请参阅此 SO 问题/答案:std::enable_if to conditionally compile a member function

  2. 引用cppreference.com:

一个常见的错误是声明两个仅不同的函数模板 在他们的默认模板参数中。这是非法的,因为默认 模板参数不是函数模板签名的一部分,并且 声明两个具有相同签名的不同函数模板是 非法。

这导致以下代码:

#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

【讨论】:

  • 我注意到您在实际示例中使用了-std=c++14,并且当我使用-std=c++11 时,它也无法编译。什么变化让这在 14 年而不是 11 年成为可能?
  • @user1476176 我将代码更新为与 C++11 兼容;唯一的变化是使用std::enable_if 而不是std::enable_if_t
【解决方案3】:

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

#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

【讨论】:

    猜你喜欢
    • 2013-01-14
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多