2011-09-27 101 views
4

我有一个模板,template <typename T> class wrapper,我想专门基于typename T::context_type的存在。如果声明了typename T::context_type,那么wrapper<T>实例化的构造函数和赋值运算符重载应接受强制参数typename T::context_type。此外,wrapper<T>对象会将“上下文”存储在成员数据中。如果typename T::context_type不存在,那么wrapper<T>的构造函数和赋值运算符重载只需要少一个参数,并且不会有其他数据成员。是否有可能基于模板类型参数的嵌套typedef的存在来专门化模板定义?

这可能吗?我可以在不更改config1config2main()的定义的情况下编译以下代码:

#include <iostream> 

template <typename T, bool context_type_defined = true> 
class wrapper 
{ 
public: 
    typedef typename T::context_type context_type; 

private: 
    context_type ctx; 

public: 
    wrapper(context_type ctx_) 
     : ctx(ctx_) 
    { 
     std::cout << "T::context_type exists." << std::endl; 
    } 
}; 

template <typename T> 
class wrapper<T, false> 
{ 
public: 
    wrapper() { 
     std::cout << "T::context_type does not exist." << std::endl; 
    } 
}; 

class config1 { 
public: 
    typedef int context_type; 
}; 

class config2 { 
public: 
}; 

int main() 
{ 
    wrapper<config1> w1(0); 
    wrapper<config2> w2; 
} 

回答

3

是的,这是可能的。过去我通过使用一些元编程技巧来实现这种行为。基本构建块如下:

BOOST_MPL_HAS_XXX_TRAIT_DEF定义一个元函数谓词,如果参数是类类型并且具有给定名称的嵌套类型(在您的情况下为context_type),则该元函数谓词将计算为真类型。

http://www.boost.org/doc/libs/1_47_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

Boost.EnableIf,定义基于先前定义的性状特。

http://www.boost.org/libs/utility/enable_if.html#见3.1启用模板类特


请注意,您可能能够获得这种行为直接与SFINAE工作,这样的事情可能工作:

template< typename T, typename Context = void > 
class wrapper { ... }; // Base definition 

template< typename T > 
class wrapper< T, typename voif_mfn< typename T::context_type >::type > { ... }; // Specialization 

然而,我就像基于特征的解决方案的表现力和使能力一样。

+0

你需要做的像'类型名称void_ :: type'来确保专业化是有效的*和*第二个参数匹配默认参数'void'。 –

+0

@Luc Danton:我后面的实际想法是,由于SFINAE的缘故,没有内部'context_type'的类型'T'不会选择专业化。 –

+0

对于那些没有嵌套类型的类型来说,特化将是无效的,但是如果'T :: context_type'是'int',那么特化是'wrappper '并且不匹配主模板'包装',这就是为什么我的意思是“它不匹配”。 –

4

这是可能的,并有很多方法来实现这一点。他们都应该回到has_type的特征类别has_type<T>::value,如果成员typedef存在,则返回true,否则返回false。我们假设我们已经有了这个特质类。那么这里有一个解决方案,使用C++模板11别名:

template <typename T, bool> class FooImpl 
{ 
    // implement general case 
}; 

template <typename T> class FooImpl<T, true> 
{ 
    // implement specific case 
}; 

template <typename T> using Foo = FooImpl<T, has_type<T>::value>; // C++11 only 

现在让typetrait:

template<typename T> 
struct has_type 
{ 
private: 
    typedef char      yes; 
    typedef struct { char array[2]; } no; 

    template<typename C> static yes test(typename C::context_type*); 
    template<typename C> static no test(...); 
public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

如果你没有C++ 11,或者如果你不这样做想要重写整个班级,你可以使这种区分更加细化,例如通过使用std::enable_ifstd::conditional等发表评论,如果你想要一些具体的例子。

+0

感谢您的回答。当C++ 11可用于我的项目时,模板别名看起来就像是要走的路。但现在,我仅限于C++ 03。目前我正试图获得@ K-ballo的建议,但事实证明这很困难。 –

+0

@DanielTrebbien您实际上并不需要模板别名来使用此解决方案。你可以使用古老的继承技巧:'template struct Foo:FooImpl :: value> {};' –

+0

@David:但是在新的派生类中使用正确的构造函数会很困难。这就是为什么我没有从一开始就继承一些继承......我认为你可以单独切换构造函数的唯一方法是使用'enable_if'。 –

1

使用@K-ballo's answer,我写了下面:

namespace detail { 
BOOST_MPL_HAS_XXX_TRAIT_DEF(context_type) 
} 

template <typename T, typename Enable = void> 
class wrapper 
{ 
public: 
    wrapper() { 
     std::cout << "T::context_type does not exist." << std::endl; 
    } 
}; 

template <typename T> 
class wrapper<T, typename boost::enable_if<detail::has_context_type<T> >::type> 
{ 
public: 
    typedef typename T::context_type context_type; 

private: 
    context_type ctx; 

public: 
    wrapper(context_type ctx_) 
     : ctx(ctx_) 
    { 
     std::cout << "T::context_type exists." << std::endl; 
    } 
}; 

现在,示例代码编译和输出:

 
T::context_type exists. 
T::context_type does not exist.