2011-02-27 141 views
6

我通常会声明我的类和模板,然后在(在当前的头文件中)定义它们的方法。我发现这样更容易阅读。那么,我遇到了一个案例,我无法弄清楚一个工作类型签名是否在一个不符合类的定义中使用。下面是我在做什么一个简单的例子,说明该问题:类型的enable_if'ed模板模板构造函数的签名?

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     , enable_if< is_iterator<Iterator> > 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    , WHAT_GOES_HERE? 
    > 
Foo<T>::Foo(U f, U l) 
{ ... } 

我已经尝试了一些在WHAT_GOES_HERE插槽的东西,试图得到一个匹配的签名,我一直失败。我需要使用enable_if来区分两个T类型对象传入的情况,以及一个传入一对Iterator时的情况。如果模板化的构造函数是在主模板内部定义的,那么代码就可以正常工作,而代码目前是这样做的,但我宁愿将该定义移动到声明之外。

编辑:我要指出,我不能只重复使用enable_if < ...>的定义,因为enable_if < ...>指定其类型,你不能做一个缺省值一个定义也不是一个声明。

+1

你真的需要SFINAE这个?如果您只声明第二个构造函数为'template Foo(U first,U last);',如果调用者传递两个类型为T的对象,则仍然会选择第一个构造函数。 – 2011-02-27 20:21:47

+0

类型T通常是一种算术类型,我希望在T无符号时能够传入整数,反之亦然,并且没有调用模板构造函数(在使用enable_if之前发生了这种情况) – swestrup 2011-02-27 20:29:13

+0

实际上,你根本没有分配一个默认值。你的模板的第二个参数是'enable_if ''。有点像如果你期望一个'int'。它不应该编译,当然也不可能使用。 – 2011-02-27 20:33:42

回答

3

我不会这么做。这里的变化我会做:

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
     , typename enable_if<is_iterator<Iterator> >::type* = 0 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    > 
Foo<T>::Foo(U f, U l, typename enable_if< is_iterator<U> >::type*) 
{ ... } 

这是直出的enable_if的文件。

+0

有趣的是,我已经多次阅读了enable_if文档,并且我从来没有想到将构造函数作为嵌套函数处理......如果这有效,那么它肯定是可以接受的。 – swestrup 2011-02-27 20:35:12

+0

请参阅http://www.boost.org/doc/libs/1_46_0/libs/utility/enable_if。html第3节:“构造函数和析构函数没有返回类型;只有一个额外的参数。” – 2011-02-27 20:38:40

+0

谢谢!那的确有诀窍。这是否意味着我之前尝试的是不可能的,因为没有办法给出满足C++的类型签名? – swestrup 2011-02-27 20:46:44

2

这是你想要完成的吗? [我没有is_iterator类型特征,因此我使用C++ 0x类型特征和实用程序库重新编写了您的示例。它应该以与TR1和Boost库相同的方式工作。]

#include <utility> 
#include <type_traits> 

template <typename T> 
struct S 
{ 
    // Constructor (1) 
    S(T, T); 

    // Constructor (2) 
    template <typename U> 
    S(U, U, typename std::enable_if<std::is_integral<U>::value>::type* = 0); 
}; 

template <typename T> 
S<T>::S(T, T) 
{ } 

template <typename T> 
template <typename U> 
S<T>::S(U, U, typename std::enable_if<std::is_integral<U>::value>::type*) 
{ } 

int main() 
{ 
    S<double> a(1.0, 2.0); // uses (1) 
    S<double> b(1, 2);  // uses (2) 
} 
+0

是的,这或多或少是正确的。我必须写我自己的is_iterator。我不知道为什么它不是标准的提升。 – swestrup 2011-02-28 22:19:31

1

,你可以做最简单的是:

template<class Iterator> 
Foo 
    (Iterator first 
    , typename enable_if<is_iterator<Iterator>, Iterator>::type last 
); 
1
template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template <class Iterator 
     ,  class = typename std::enable_if 
         <is_iterator<Iterator>::value> 
         ::type 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ } 

template <class T> 
template 
    < class U 
    , class > 
Foo<T>::Foo(U f, U l) 
{ } 
+0

我试过这个,它不起作用。我收到一条消息,说该定义与我的模板中的任何声明不匹配。虽然,我必须说,这**应该**工作! – swestrup 2011-02-27 20:55:18

+0

我用g ++ - 4.4和clang测试了它。不过,我的测试中有-std = C++ 0x,现在我仔细检查了一下,我认为这是必需的。如果没有它,clang会给出警告:警告:函数模板的默认模板参数是C++ 0x扩展名[-WC++ 0x扩展名] ,class = typename std :: enable_if – 2011-02-27 21:13:25

+0

奇怪的是,您的示例可以工作,我用我的编译器尝试它。但是,当我在真实模板上尝试相同的东西时,出现错误。我一定在做错事... – swestrup 2011-02-28 22:34:40