那是因为模板类中的模板函数具有two一组模板参数,而不是一个。因此,“正确”的形式是:
template<typename T, int N>
class A
{
public:
void f(void);
template<typename std::enable_if<N == 2, void>::type* = nullptr>
void g(void);
};
template<typename T, int N> // Class template.
template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
inline void A<T, N>::g()
{
std::cout << "g()\n";
}
查看实际效果here http://coliru.stacked-crooked.com/a/c0689d66b1264339.
[注意,这不是actually正确,原因在这个答案的底部解释。如果它会破裂N != 2
.]
如果您愿意,请继续阅读以获得解释。
还在我这儿?好的。让我们检查一下每种情况,好吗?
-
定义A<T, N>::g()
外部A
:
template<typename T, int N>
class A
{
public:
void f(void);
void g(void);
};
template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
inline void A<T, N>::g()
{
std::cout << "g()\n";
}
在这种情况下,A<T, N>::g()
的模板声明不匹配A
的模板声明。因此,编译器会发出错误。此外,g()
本身没有模板化,因此模板不能在不改变的情况下分为类模板和函数模板A
的定义。
template<typename T, int N>
class A
{
public:
void f(void);
// Here...
template<typename std::enable_if<N == 2, void>::type* = nullptr>
void g(void);
};
// And here.
template<typename T, int N> // Class template.
template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
inline void A<T, N>::g()
{
std::cout << "g()\n";
}
-
定义A<T, N>::g()
inside A
:
template<typename T, int N>
class A
{
public:
void f(void);
template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
void g()
{
std::cout << "g()\n";
}
};
在这种情况下,由于g()
是内联定义的,它隐式地具有A
的模板参数,无需手动指定。所以,g()
实际上是:
// ...
template<typename T, int N>
template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
void g()
{
std::cout << "g()\n";
}
// ...
在这两种情况下,对于g()
为了拥有自己的模板参数,同时作为模板类的成员,函数模板参数必须与类模板参数分开。否则,函数的类模板将与类不匹配。
现在我们已经介绍了这一点,我应该指出 SFINAE 只关注即时模板参数。因此对于g()
与 SFINAE 一起使用N
, N
需要是它的模板参数;否则,如果您尝试调用,则会收到错误消息,例如,A<float, 3>{}.g()
。如有必要,这可以通过中介来完成。
此外,您还需要提供以下版本g()
当N != 2
。这是因为 SFINAE 仅当该函数至少有一个有效版本时才适用;如果没有版本g()
可以调用,然后将发出错误并且不会执行 SFINAE。
template<typename T, int N>
class A
{
public:
void f(void);
// Note the use of "MyN".
template<int MyN = N, typename std::enable_if<MyN == 2, void>::type* = nullptr>
void g(void);
// Note the "fail condition" overload.
template<int MyN = N, typename std::enable_if<MyN != 2, void>::type* = nullptr>
void g(void);
};
template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN == 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
std::cout << "g()\n";
}
template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN != 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
std::cout << "()g\n";
}
如果这样做,我们可以通过让中介承担繁重的工作来进一步简化事情。
template<typename T, int N>
class A
{
public:
void f(void);
template<bool B = (N == 2), typename std::enable_if<B, void>::type* = nullptr>
void g(void);
template<bool B = (N == 2), typename std::enable_if<!B, void>::type* = nullptr>
void g(void);
};
// ...
查看实际效果here http://coliru.stacked-crooked.com/a/5b2b888c8a77889d.