C ++模板即时:直接使用enable_if,或使用辅助类

时间:2015-03-11 11:52:19

标签: c++ templates c++11 enable-if

此代码可以正常工作:

#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的可读性,因此,让我们进行攻击,例如,使用辅助类进行第二次enable_if攻击:

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>

我不明白为什么专业化失败不会被忽略以获得第一个专业化(我知道它有效)。

2 个答案:

答案 0 :(得分:5)

错误不在&#34;直接上下文&#34;模板参数推导,因此SFINAE不适用(即它是一个错误,而不是替换失败)。

C ++标准并没有真正定义&#34;直接上下文&#34;但是我试图在接受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基类声明它时才会出现。

答案 1 :(得分:4)

简介

SFINAE 仅在替换错误位于模板实例化的立即上下文中时才适用,如果在所述模板的主体中发生替换错误,则程序格式错误


解决方案

要么在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>
{ };