类模板构造函数中的 SFINAE
Posted
技术标签:
【中文标题】类模板构造函数中的 SFINAE【英文标题】:SFINAE in class template constructors 【发布时间】:2013-04-30 13:13:33 【问题描述】:我正在尝试使用模板和 SFINAE 制作一些东西,我是其中的初学者。我正在浪费大量时间来完成每件最简单的事情。你能帮我理解它是如何工作的吗?
C 的构造函数接受一个 T 参数,它可以是 A 或 B,但在这两种情况下具有不同的行为。我无法向你展示我试图这样做的一切。这是我认为最不愚蠢的方式。
template<typename T> class A
public: A() ;
template<typename T> class B
public: B() ;
template<typename T> struct enable_if_A ;
template<typename T> struct enable_if_A< A<T> > typedef A<T> type;;
template<typename T> struct enable_if_B ;
template<typename T> struct enable_if_B< B<T> > typedef B<T> type;;
template<typename T,typename... Ts> class C
public:
C(typename enable_if_A<T>::type const &p)cout << "A" << endl;
C(typename enable_if_B<T>::type const &p)cout << "B" << endl;
;
// ...
A<float> a;
B<float> b;
C<A<float> > ca(a); // error: no type named ‘type’ in ‘struct enable_if_B<A<float> >'
C<B<float> > cb(b); // error: no type named ‘type’ in ‘struct enable_if_A<B<float> >'
注意:我使用的是 g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1。我应该升级它吗?
谢谢
编辑:有关更多详细信息,我也尝试过(除其他外):
template<typename T,typename... Ts> class C
public:
template<>
C(typename enable_if_A<T>::type const &p)cout << "A" << endl;
template<>
C(typename enable_if_B<T>::type const &p)cout << "B" << endl;
;
//explicit specialization in non-namespace scope ‘class bcifs::C<T, Ts>’
//////////////////////////////////////////////////////////////////////////
template<typename T,typename... Ts> class C
public:
template<typename E=void>
C(typename enable_if_A<T>::type const &p)cout << "A" << endl;
template<typename E=void>
C(typename enable_if_B<T>::type const &p)cout << "B" << endl;
;
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >'
//////////////////////////////////////////////////////////////////////////
template<typename T> struct enable_if_A ;
template<typename T> struct enable_if_A< A<T> > typedef void type;;
template<typename T> struct enable_if_B ;
template<typename T> struct enable_if_B< B<T> > typedef void type;;
template<typename T,typename... Ts> class C
public:
template<typename E=void>
C(T const &p);
C<typename enable_if_A<T>::type>(T const &p)cout << "A" << endl;
C<typename enable_if_B<T>::type>(T const &p)cout << "B" << endl;
;
// error: invalid declarator before ‘(’ token
//////////////////////////////////////////////////////////////////////////
template<typename T> class C
public:
template<>
C(T const &p,typename enable_if_A<T>::type * = 0)cout << "A" << endl;
template<>
C(T const &p,typename enable_if_B<T>::type * = 0)cout << "B" << endl;
;
// error: explicit specialization in non-namespace scope ‘class C<T>’
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >’
//////////////////////////////////////////////////////////////////////////
template<typename T> class C
public:
template<typename U>
C(T const &p,typename enable_if_A<T>::type * = 0)cout << "A" << endl;
template<typename U>
C(T const &p,typename enable_if_B<T>::type * = 0)cout << "B" << endl;
;
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >’
// error: no matching function for call to ‘C<A<float> >::C(A<float>&)’
//////////////////////////////////////////////////////////////////////////
template<typename T> struct enable_if_A ;
template<typename T> struct enable_if_A< A<T> > typedef void type;;
template<typename T> struct enable_if_B ;
template<typename T> struct enable_if_B< B<T> > typedef void type;;
template<typename T> class C
public:
template <typename U>
C(A<U> const & r, void* _ = 0);
;
template <typename T>
template <typename U>
C<T>::C<T>(A<U> const & r, typename enable_if_A<U>::type* _ = 0)
cout << "A" << endl;
// error: ISO C++ forbids declaration of ‘C’ with no type [-fpermissive]
// error: function template partial specialization ‘C<T>’ is not allowed
// error: no ‘int C<T>::C(const A<U>&, typename enable_if_A<U>::type*)’ member function declared in class ‘C<T>’
// C<T>::C<U>(... does the same
很抱歉,我从未设法运行您的解决方案。我终于找到了:
// dummy-function-parameter-ed version :
template<typename T> class C
public:
template <typename U>
C(A<U> const &r,typename enable_if<is_same<A<U>,T>::value>::type* = 0)cout << "A" << endl;
template <typename U>
C(B<U> const &r,typename enable_if<is_same<B<U>,T>::value>::type* = 0)cout << "B" << endl;
;
// and the dummy-template-parameter-ed version :
template<typename T> class C
public:
template<typename U,typename E = typename enable_if<is_same<A<U>,T>::value>::type>
C(A<U> &r)cout << "A" << endl;
template<typename U,typename E = typename enable_if<is_same<B<U>,T>::value>::type>
C(B<U> &r)cout << "B" << endl;
;
【问题讨论】:
SFINAE 基于函数模板重载。也就是说,您需要有重载的函数模板(可能失败的必须是模板),而不仅仅是重载的函数。如果非模板函数包含无效类型,则您的程序格式错误。如果该函数是函数模板,则应用特殊的 SFINAE 规则并选择重载机制中的下一个可行函数。 @DyP:不一定是function模板,也可以应用于class模板,但必须应用于template 直接,而不是模板的任何成员。 @DavidRodríguez-dribeas 那会选择(部分)专业化?我认为我从未见过将 SFINAE 应用于类模板而不是函数模板.. @DyP:查看boost enable_if 文档中的启用类专业化部分。它有几个例子,其中 enable if 用于丢弃一些特化以支持其他特化,所有这些都应用于类型,而不是函数。虽然它们是专业化,但它是 SFINAE,而不是基于部分排序的简单好选择 @DavidRodríguez-dribeas +1 不错。虽然它是基于部分排序(+精确匹配)的选择,甚至与函数重载决议密切相关(相同的部分排序)[temp.class.order] 【参考方案1】:template<typename T,typename... Ts> class C
public:
C(typename enable_if_A<T>::type const &p)cout << "A" << endl;
C(typename enable_if_B<T>::type const &p)cout << "B" << endl;
;
这是错误的,但您已经知道了 :) 原因是 SFINAE 只能应用于模板级别,但您正试图将其应用于模板的成员。也就是说,你上面模板中的SFINAE只能应用于不同的C<T>
类型,而不能应用于C<T>
的构造函数。
为了能够将 SFINAE 应用于构造函数,您需要将构造函数设为模板。但在您的情况下,这将导致另一个限制。构造函数是不能提供模板参数的特殊函数(即使构造函数是模板化的),这意味着必须从调用位置推导出模板类型。但是嵌套类型是不可推演的……
您可以通过更改构造函数的签名来解决此限制:
template <typename T>
template <typename U>
C<T>::C<U>(A<U> const & r, typename enable_if_A<U>::type* _ = 0)
// ...
在这种情况下,C
类是一个模板,它有一个模板化构造函数,该构造函数采用 A<U>
,它只能用于 enable_if_A<U>::type
确实是一个类型的类型。类型可以通过第一个参数在调用的地方推导出来,推导出的类型U
将被第二个参数替换。如果替换失败,模板化的构造函数将被丢弃。
上述解决方案与 C++03 兼容。如果你有一个 C++11 编译器,你可以在不需要构造函数的额外参数的情况下执行相同的操作(即不添加额外参数;如果我的语法正确,则不是 100% :)):
template <typename T>
template <typename U, typename _ = typename enable_if_A<U>::type>
C<T>::C<U>(U const &) ...
【讨论】:
我想我必须在类模板声明之外编写这个函数定义。但我仍然做不到(我把我的尝试之一放在我的帖子末尾。对不起,我尝试了很长时间,以至于我什么都不懂了。我有错误:ISO C++ 禁止声明' C' with no type [-fpermissive] // 错误:不允许函数模板部分特化 'CT
是固定的,因此没有替换失败,因此没有SFinae。关于 binding 函数参数到成员模板参数,它与任何其他类型没有什么不同,所以是的,您可以像 is_same<T2,int>
一样执行 is_same<T1,T2>
或应用任何与封闭模板的参数相关或不相关的其他元函数。【参考方案2】:
如果您不坚持使用 SFINAE,您可以通过使用具有部分特化的间接构造函数来轻松解决此问题,如下所示:
#include <iostream>
using namespace std;
template <typename T>
struct C
T val;
C() init(); ;
void init() ;
;
template<> void C<string>::init() val = "STR";
template<> void C<int>::init() val = 5;
int main ()
C<int> x;
C<string> y;
cout << y.val << endl << x.val << endl;
此示例使用 int 和 string 而不是您的类型 A 和 B,但这应该没有区别(除了它使该示例可编译)。
【讨论】:
以上是关于类模板构造函数中的 SFINAE的主要内容,如果未能解决你的问题,请参考以下文章