C++ 模板实例化:直接使用 enable_if,或使用辅助 class
C++ template instantations: using enable_if directly, or with an auxiliary class
这段代码工作正常:
#include <type_traits>
using namespace std;
enum class Type : char { Void };
struct FieldA { static constexpr Type type = Type::Void; };
template<typename Field> struct Signal {};
template<typename Field> struct SignalA : public Signal<Field> {};
struct SignalB : public Signal<void> {};
struct DerivedB : public SignalB {};
template<typename Signal, typename = void> struct Apply;
template<typename Field>
struct Apply<SignalA<Field>, typename std::enable_if<Field::type == Type::Void>::type> {};
template<typename Signal_t>
struct Apply<Signal_t, typename enable_if<is_base_of<SignalB, Signal_t>::value>::type>
{};
int main ()
{ Apply<SignalA<FieldA> > a; }
但我想要的是提高可读性删除长 enable_if
s,所以,让我们攻击,例如,第二个 enable_if
与辅助 class:
template<typename Signal>
struct IsBaseOfB
{ using type = typename enable_if<is_base_of<SignalB, Signal>::type; };
并用它更改第二个 Apply
偏特化:
template<typename Signal_t>
struct Apply<Signal_t, typename IsBaseOfB<Signal_t>::type>
{};
即使唯一可能的专业化是第一个,gcc
也会抛出以下错误:
main.cpp: In instantiation of 'struct IsBaseOfB<SignalA<FieldA> >':
main.cpp:21:78: error: no type named 'type' in 'struct std::enable_if<false, void>'
{ using type = typename enable_if<is_base_of<SignalB, Signal_t>::value>::type; };
这很明显,因为 enable_if
条件不适合 SignalA<FieldA>
。
我不明白为什么不忽略专业化失败以获得第一个专业化(我知道它有效)。
错误不在模板参数推导的 "the immediate context" 中,因此 SFINAE 不适用(即这是一个错误,而不是替换失败)。
C++ 标准并没有真正定义 "immediate context" 但我试图在 What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?
的已接受答案中给出一个简单的解释
简而言之,问题是编译器看到 Apply<SignalA<FieldA>>
并愉快地将模板参数替换为 struct Apply<Signal_t, typename IsBaseOfB<Signal_t>::type>
,这没有发生错误,因为 IsBaseOf
确实 有一个嵌套的 type
成员,但是这会触发 IsBaseOf::type
的实例化,这是错误的。错误不在推导的直接上下文中(它在 IsBaseOf
主体的其他地方,它被实例化为副作用)。
另一种看待问题的方式是 IsBaseOf::type
是无条件声明的,而不仅仅是当 is_base_of
特征为真时,所以总是 声明 作为一种类型...但有时它的 定义 格式不正确。推导找到声明并继续超过 SFINAE 应用的点,然后在需要 IsBaseOf::type
的定义时发现致命错误。
您可以使用别名模板(如 Filip 的回答所示)或使用继承来解决它,因此 IsBaseOf::type
仅有条件地存在:
template<typename Signal>
struct IsBaseOfB : enable_if<is_base_of<SignalB, Signal>::value>
{ };
这样,嵌套的 type
成员仅在 enable_if
基础 class 声明它时出现。
简介
SFINAE 仅适用于替换错误发生在模板实例化的 直接上下文 中,如果替换错误发生在所述程序格式错误的模板。
- What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?
解决方案
要么使替换错误发生在 template<typename> struct IsBaseOfB
实例化的直接上下文中,要么简单地定义一个 模板别名声明 ,它将等效于你相当冗长的特点。
模板别名声明(推荐)
template<typename Signal>
using IsBaseOfB = enable_if<is_base_of<SignalB, Signal>::value>;
使用继承
template<typename Signal>
struct IsBaseOfB
: enable_if<is_base_of<SignalB, Signal>::value>
{ };
这段代码工作正常:
#include <type_traits>
using namespace std;
enum class Type : char { Void };
struct FieldA { static constexpr Type type = Type::Void; };
template<typename Field> struct Signal {};
template<typename Field> struct SignalA : public Signal<Field> {};
struct SignalB : public Signal<void> {};
struct DerivedB : public SignalB {};
template<typename Signal, typename = void> struct Apply;
template<typename Field>
struct Apply<SignalA<Field>, typename std::enable_if<Field::type == Type::Void>::type> {};
template<typename Signal_t>
struct Apply<Signal_t, typename enable_if<is_base_of<SignalB, Signal_t>::value>::type>
{};
int main ()
{ Apply<SignalA<FieldA> > a; }
但我想要的是提高可读性删除长 enable_if
s,所以,让我们攻击,例如,第二个 enable_if
与辅助 class:
template<typename Signal>
struct IsBaseOfB
{ using type = typename enable_if<is_base_of<SignalB, Signal>::type; };
并用它更改第二个 Apply
偏特化:
template<typename Signal_t>
struct Apply<Signal_t, typename IsBaseOfB<Signal_t>::type>
{};
即使唯一可能的专业化是第一个,gcc
也会抛出以下错误:
main.cpp: In instantiation of 'struct IsBaseOfB<SignalA<FieldA> >':
main.cpp:21:78: error: no type named 'type' in 'struct std::enable_if<false, void>'
{ using type = typename enable_if<is_base_of<SignalB, Signal_t>::value>::type; };
这很明显,因为 enable_if
条件不适合 SignalA<FieldA>
。
我不明白为什么不忽略专业化失败以获得第一个专业化(我知道它有效)。
错误不在模板参数推导的 "the immediate context" 中,因此 SFINAE 不适用(即这是一个错误,而不是替换失败)。
C++ 标准并没有真正定义 "immediate context" 但我试图在 What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?
的已接受答案中给出一个简单的解释简而言之,问题是编译器看到 Apply<SignalA<FieldA>>
并愉快地将模板参数替换为 struct Apply<Signal_t, typename IsBaseOfB<Signal_t>::type>
,这没有发生错误,因为 IsBaseOf
确实 有一个嵌套的 type
成员,但是这会触发 IsBaseOf::type
的实例化,这是错误的。错误不在推导的直接上下文中(它在 IsBaseOf
主体的其他地方,它被实例化为副作用)。
另一种看待问题的方式是 IsBaseOf::type
是无条件声明的,而不仅仅是当 is_base_of
特征为真时,所以总是 声明 作为一种类型...但有时它的 定义 格式不正确。推导找到声明并继续超过 SFINAE 应用的点,然后在需要 IsBaseOf::type
的定义时发现致命错误。
您可以使用别名模板(如 Filip 的回答所示)或使用继承来解决它,因此 IsBaseOf::type
仅有条件地存在:
template<typename Signal>
struct IsBaseOfB : enable_if<is_base_of<SignalB, Signal>::value>
{ };
这样,嵌套的 type
成员仅在 enable_if
基础 class 声明它时出现。
简介
SFINAE 仅适用于替换错误发生在模板实例化的 直接上下文 中,如果替换错误发生在所述程序格式错误的模板。
- What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?
解决方案
要么使替换错误发生在 template<typename> struct IsBaseOfB
实例化的直接上下文中,要么简单地定义一个 模板别名声明 ,它将等效于你相当冗长的特点。
模板别名声明(推荐)
template<typename Signal>
using IsBaseOfB = enable_if<is_base_of<SignalB, Signal>::value>;
使用继承
template<typename Signal>
struct IsBaseOfB
: enable_if<is_base_of<SignalB, Signal>::value>
{ };