在参数列表中使用std :: enable_if

时间:2015-01-07 18:21:05

标签: c++ templates enable-if

在C ++中,请考虑以下示例:

template <typename first, typename... params> struct q;

template <typename first> struct q <first>
{
    q()
    {
        cout<<"x"<<endl;
    }
};

template <typename first, typename... params> struct q
{
    q()
    {
        cout<<"x";
        q <params...> ();
    }
};

int main()
{
    q <int, int> ();
    q <int, int, enable_if<true, bool>::type> ();
    // q <int, int, enable_if<false, bool>::type> ();
}

我定义了一个接受任意数量参数的模板结构。然后我用一组参数实例化它。每个构造函数都将构建一个新的q,其所有参数都是第一个。只有一个参数的q不会再调用递归。

每次调用q的构造函数时,它都会打印出一个“x”。因此,main的第一行将导致程序打印出“xx”,而第二行将打印出“xxx”,因为enable_if实际上已启用。现在,如果第三行会导致我的程序打印出“xx”(即:构建一个q),我将非常感激。可悲的是,我得到的是错误

No template named 'type' in std::enable_if <false, bool>

我该怎么做才能纠正我的例子并使其有效?

谢谢

2 个答案:

答案 0 :(得分:5)

您误解了std::enable_if的使用。 (我们中的许多人一开始就这样做了)。 这导致你提出一个技术错误的问题,而不是更多 一般你想要回答。您想要回答的问题是我的模板如何使用或 忽略模板参数,仅基于编译时条件?,而不是 一个糟糕的。

您的印象是std::enable_if<Cond,T>::type是模板表达式 当TCond时将会true实例化,并且当Cond实例化为 Cond是假的。

truestd::enable_if<true,T>时这是正确的,因为std::enable_if<true,T> { typedef T type; }; 的定义如下:

std::enable_if<false,T>

std::enable_if<false,T> {}; 的定义如下:

std::enable_if<false,T>::type

请参阅这些定义here

因此T 不会消失;对于任何type, 它成为一个不正确的,无法编译的,对成员类型std::enable_if<false,T>的引用 std::enable_if,不存在。它成为编译器错误。

因此template<bool Cond> typename std::enable_if<Cond,int>::type foo(int i) // A { return i*i; } template<bool Cond> typename std::enable_if<!Cond,int>::type foo(int i) // B { return i + i; } 可用于实例化模板类或 根据某些的编译时间值,可能或不可能运行 布尔常数。而且由于编译器将不考虑任何问题 模板的不可行的实例化,只要有可行的替代方案, 因此,它可以用于在备选之间进行编译时选择 模板类或函数的实现,基于这样的条件,例如

foo<some_bool_constant>(i)

如果some_bool_constanttruesome_bool_constant会产生 i平方 如果falseB我加倍,因为在第一种情况下 A重载具有格式错误的实例化,在第二种情况下 这是foo<some_bool_constant>(x)重载变得格格不入。

替代重载使编译器可以选择要考虑的实例化 每当它看到some_bool_constant,其中一个变得不合格而另一个变得可行, 取决于std::enable_if的真值。它选择了在每种情况下都可行的那个。这个 行为被称为SFINAE,那就是 在理解q <int, int, enable_if<false, bool>::type> () 之前,您需要了解的内容。

但是你没有替代实例:

std::enable_if<false,bool>::type

“取决于 false ”的值。 false 无条件地意味着 false ,所以这只是格式错误 代码。

并考虑:即使q <int, int, > () 以某种方式消失了,也会离开:

std::enable_if

仍然形成错误。

  

我该怎么做才能纠正我的例子并使其有效?

事实上,这并不需要太多,q<First, ... Rest>没有特色。让我们来解决真正的问题:我的模板如何使用或 忽略模板参数,仅基于编译时条件?

您需要以这种方式定义模板accept_if<bool Cond, typename T>和其他模板q 适用于/*(Iq)*/ q<A0,....,Aj,....,Ak> // 0 <= j <= k 的任何实例化:

Aj

其中accept_if<some_bool_constant,t>::type = some_bool_constant,如果true == q<A0,....,t,....,Ak> (Iq)的效果与:

完全相同
some_bool_constant

如果false == q<A0,....,Ak> 那么 (Iq)将产生与以下相同的效果:

some_bool_constant

,而不依赖于accept_if<some_bool_constant,t>::type 的任何运行时测试。 (在(Iq)中, '....'只是可能论证的示意性占位符:无事可做 使用C ++包或elipses。)

但是false必须在编译时解析为某些类型说明符 或者代码格式不正确。它无法解决任何问题。所以在void情况下,它必须解析为一个类型 说明(Iq)的实例化将指定 no data-type

现在拥有一个指定无数据类型的类型说明符的引人注目的实用程序 在C / C ++中很久以前就被认为是C.的第一个ANSI标准 类型说明符是q

从您的评论中我们了解到void的模板参数 表示构造函数的实例化应提取的数据类型序列 来自参数传递的来源。在这种情况下,void理想地适合该法案 作为一个类型说明符,表示无需执行任何操作。您不能声明accept_if对象, 更不用说从某个地方提取一个。

因此,我建议您定义template<bool Cond, typename T> using accept_if = std::conditional<Cond,T,void>::type;

accept_if<true,T>

即。 T = accept_if<false,T>void = q。阅读 std::conditional

使用此定义,您只需添加到现有的专业化 template<> struct q<void> {}; 再一次:

q<void>

#include <type_traits> #include <iostream> template<bool Cond, typename T> using accept_if = typename std::conditional<Cond,T,void>::type; template <typename first, typename... params> struct q; template<> struct q<void> {}; template <typename first> struct q <first> { q() { std::cout << "x" << std::endl; } }; template <typename first, typename... params> struct q { q() { std::cout << "x"; q<params...>(); } }; int main() { q<int, int>(); q<int, int, accept_if<true,bool>>(); q<int, int, accept_if<false,bool>>(); return 0; } 一个什么都不做的默认构造函数。这是一个示例程序 像你自己的,但形式良好,这说明了这个解决方案:

xx
xxx
xx

输出是你想要的:

Aj

在不同的情况下,您甚至可能需要void = struct ignore {};(Iq)中忽略 。但是既然我们可以随意创建类型,那么你可以 始终创建一个与您的模板所有不同的类型 不能忽视,比如说:template<bool Cond, typename T> using accept_if = std::conditional<Cond,T,ignore>::type; 然后你要定义:

template<> struct q<ignore> {};

并专门化:

{{1}}

答案 1 :(得分:0)

enable_if旨在用于可以使用SFINAE的上下文中,这意味着在模板中。

在非模板上下文(或不依赖于模板参数的上下文)中,当条件为false时,引用enable_if<false, T>::type是错误的,因为该成员不存在。

  

我该怎么做才能纠正我的例子并使其有效?

我不明白你要做什么。你期望enable_if<false, bool>::type是什么?没有这种类型,您认为如何使用它?