此代码可以正常工作:
#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>
。
我不明白为什么专业化失败不会被忽略以获得第一个专业化(我知道它有效)。
答案 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>
{ };