在参数列表中使用 std::enable_if
Using std::enable_if in a parameters list
在 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>
我该怎么做才能更正我的示例并让它发挥作用?
谢谢
enable_if
用于可以使用 SFINAE 的上下文,这意味着在模板内部。
在非模板上下文(或不依赖于模板参数的上下文)中,当条件为假时,引用 enable_if<false, T>::type
是错误的,因为该成员不存在。
What could I do to correct my example and make it work?
我不明白你想做什么。您期望 enable_if<false, bool>::type
是什么?没有这个类型,你觉得怎么用?
你误解了std::enable_if
的用法。 (我们中的许多人开始了)。
这导致您问了一个技术上被误导的问题,而不是更多
您想要回答的一般问题。您要回答的问题是 我的模板如何使用或
忽略模板参数,仅基于编译时条件?,它不是
一个坏的。
您的印象是 std::enable_if<Cond,T>::type
是一个模板表达式
当 Cond
为 true
时将实例化为 T
,当 nothing 时实例化为 nothing
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> {};
查看那些定义here
因此std::enable_if<false,T>::type
不会消失;对于任何 T
,
它成为对成员类型 type
的错误格式的、不可编译的引用
std::enable_if<false,T>
,不存在。它成为一个编译器错误。
因此std::enable_if
可用于实例化模板class或
根据某些函数的编译时值,函数可能或不可能
布尔常量。并且由于编译器将不考虑任何
如果有可行的替代方案,模板的不可行实例化,
因此,它可用于在备选方案之间进行编译时选择
基于这种条件的模板 class 或函数的实现,例如
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;
}
在这里,如果 some_bool_constant
是 true
,foo<some_bool_constant>(i)
将产生 i 平方 并且
如果some_bool_constant
是false
,我加倍,因为在第一种情况下
B
重载的实例化格式错误,在第二种情况下
是 A
重载变得格式错误。
替代重载让编译器可以选择要考虑的实例化
每次它看到 foo<some_bool_constant>(x)
,其中一个结果是错误的,另一个是可行的,
取决于 some_bool_constant
的真值。它会选择在每种情况下都可行的一个。这个
行为称为 SFINAE,并且
是你需要了解的才能理解std::enable_if
.
但是您的没有替代实例:
q <int, int, enable_if<false, bool>::type> ()
"depending on the value of false"。 false 无条件地意味着 false 所以这是错误的
代码。
并考虑:即使 std::enable_if<false,bool>::type
确实 以某种方式消失了,那也会留下:
q <int, int, > ()
仍然格式错误。
What could I do to correct my example and make it work?
其实并不需要太多,而且 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
,
(Iq) 将具有与以下完全相同的效果:
q<A0,....,t,....,Ak>
如果 some_bool_constant
== false
那么
(Iq) 将具有与以下相同的效果:
q<A0,....,Ak>
而不诉诸 some_bool_constant
的任何运行时测试。 (在 (Iq),
'....' 只是可能参数的示意性占位符:无事可做
使用 C++ 包或省略号。)
但是 accept_if<some_bool_constant,t>::type
必须在编译时解析为某些 类型说明符
或者代码格式错误。它不能解决任何问题。所以在 false
的情况下,它必须解析为一个类型
(Iq) 的实例化将作为指定 无数据类型 .
处理的说明符
现在拥有指定无数据类型的类型说明符的引人注目的实用程序
在 C/C++ 中早在很久以前就被认为是 C 的第一个 ANSI 标准。
类型说明符是 void
.
从您的评论中我们了解到 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
。阅读
std::conditional
有了这个定义,您只需添加到您现有的专业化
q
还有一个:
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
不会 在 (Iq) 中被忽略。但是因为我们可以随意创建类型,你可以
始终创建一个与您的模板完全不同的类型
一定不能忽略,说:struct ignore {};
然后你会定义:
template<bool Cond, typename T>
using accept_if = std::conditional<Cond,T,ignore>::type;
并专攻:
template<> struct q<ignore> {};
在 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>
我该怎么做才能更正我的示例并让它发挥作用?
谢谢
enable_if
用于可以使用 SFINAE 的上下文,这意味着在模板内部。
在非模板上下文(或不依赖于模板参数的上下文)中,当条件为假时,引用 enable_if<false, T>::type
是错误的,因为该成员不存在。
What could I do to correct my example and make it work?
我不明白你想做什么。您期望 enable_if<false, bool>::type
是什么?没有这个类型,你觉得怎么用?
你误解了std::enable_if
的用法。 (我们中的许多人开始了)。
这导致您问了一个技术上被误导的问题,而不是更多
您想要回答的一般问题。您要回答的问题是 我的模板如何使用或
忽略模板参数,仅基于编译时条件?,它不是
一个坏的。
您的印象是 std::enable_if<Cond,T>::type
是一个模板表达式
当 Cond
为 true
时将实例化为 T
,当 nothing 时实例化为 nothing
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> {};
查看那些定义here
因此std::enable_if<false,T>::type
不会消失;对于任何 T
,
它成为对成员类型 type
的错误格式的、不可编译的引用
std::enable_if<false,T>
,不存在。它成为一个编译器错误。
因此std::enable_if
可用于实例化模板class或
根据某些函数的编译时值,函数可能或不可能
布尔常量。并且由于编译器将不考虑任何
如果有可行的替代方案,模板的不可行实例化,
因此,它可用于在备选方案之间进行编译时选择
基于这种条件的模板 class 或函数的实现,例如
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;
}
在这里,如果 some_bool_constant
是 true
,foo<some_bool_constant>(i)
将产生 i 平方 并且
如果some_bool_constant
是false
,我加倍,因为在第一种情况下
B
重载的实例化格式错误,在第二种情况下
是 A
重载变得格式错误。
替代重载让编译器可以选择要考虑的实例化
每次它看到 foo<some_bool_constant>(x)
,其中一个结果是错误的,另一个是可行的,
取决于 some_bool_constant
的真值。它会选择在每种情况下都可行的一个。这个
行为称为 SFINAE,并且
是你需要了解的才能理解std::enable_if
.
但是您的没有替代实例:
q <int, int, enable_if<false, bool>::type> ()
"depending on the value of false"。 false 无条件地意味着 false 所以这是错误的 代码。
并考虑:即使 std::enable_if<false,bool>::type
确实 以某种方式消失了,那也会留下:
q <int, int, > ()
仍然格式错误。
What could I do to correct my example and make it work?
其实并不需要太多,而且 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
,
(Iq) 将具有与以下完全相同的效果:
q<A0,....,t,....,Ak>
如果 some_bool_constant
== false
那么
(Iq) 将具有与以下相同的效果:
q<A0,....,Ak>
而不诉诸 some_bool_constant
的任何运行时测试。 (在 (Iq),
'....' 只是可能参数的示意性占位符:无事可做
使用 C++ 包或省略号。)
但是 accept_if<some_bool_constant,t>::type
必须在编译时解析为某些 类型说明符
或者代码格式错误。它不能解决任何问题。所以在 false
的情况下,它必须解析为一个类型
(Iq) 的实例化将作为指定 无数据类型 .
现在拥有指定无数据类型的类型说明符的引人注目的实用程序
在 C/C++ 中早在很久以前就被认为是 C 的第一个 ANSI 标准。
类型说明符是 void
.
从您的评论中我们了解到 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
。阅读
std::conditional
有了这个定义,您只需添加到您现有的专业化
q
还有一个:
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
不会 在 (Iq) 中被忽略。但是因为我们可以随意创建类型,你可以
始终创建一个与您的模板完全不同的类型
一定不能忽略,说:struct ignore {};
然后你会定义:
template<bool Cond, typename T>
using accept_if = std::conditional<Cond,T,ignore>::type;
并专攻:
template<> struct q<ignore> {};