在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>
我该怎么做才能纠正我的例子并使其有效?
谢谢
答案 0 :(得分:5)
您误解了std::enable_if
的使用。 (我们中的许多人一开始就这样做了)。
这导致你提出一个技术错误的问题,而不是更多
一般你想要回答。您想要回答的问题是我的模板如何使用或
忽略模板参数,仅基于编译时条件?,而不是
一个糟糕的。
您的印象是std::enable_if<Cond,T>::type
是模板表达式
当T
为Cond
时将会true
实例化,并且当Cond
实例化为时
Cond
是假的。
true
为std::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_constant
为true
,some_bool_constant
会产生 i平方
如果false
为B
,我加倍,因为在第一种情况下
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
是什么?没有这种类型,您认为如何使用它?