简体   繁体   English

是否可以根据模板类型参数的嵌套typedef的存在来专门化模板定义?

[英]Is it possible to specialize a template definition based on the existence of a nested typedef of a template type parameter?

I have a template, template <typename T> class wrapper , that I would like to specialize based on the existence of typename T::context_type . 我有一个模板, template <typename T> class wrapper ,我想基于typename T::context_type的存在来专门化。 If typename T::context_type is declared, then the constructors and assignment operator overloads of the wrapper<T> instantiation should accept a mandatory typename T::context_type parameter. 如果声明了typename T::context_type ,那么wrapper<T>实例化的构造函数和赋值运算符重载应该接受强制typename T::context_type参数。 Additionally, wrapper<T> objects would store "context" in the member data. 此外, wrapper<T>对象将在成员数据中存储“上下文”。 If typename T::context_type does not exist, then the constructors and assignment operator overloads of wrapper<T> would take one less parameter and there would be no additional data member. 如果typename T::context_type不存在,则wrapper<T>的构造函数和赋值运算符重载将减少一个参数,并且不会有其他数据成员。

Is this possible? 这可能吗? Can I get the following code to compile without changing the definitions of config1 , config2 , and main() ? 我是否可以在不更改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;
}

Yes, it is possible. 对的,这是可能的。 I have implemented such behavior in the past by using some metaprogramming tricks. 我过去通过使用一些元编程技巧实现了这种行为。 The basic build blocks are: 基本构建块是:

BOOST_MPL_HAS_XXX_TRAIT_DEF , to define a metafunction predicate that will evaluate to a true type if the argument is of class type and has a nested type with a given name (context_type in your case). 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 http://www.boost.org/doc/libs/1_47_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

Boost.EnableIf , to define the specializations based on the previously defined trait. Boost.EnableIf ,根据先前定义的特征定义特化。

http://www.boost.org/libs/utility/enable_if.html # See 3.1 Enabling template class specializations http://www.boost.org/libs/utility/enable_if.html#请参阅3.1启用模板类专业化


Note that you may be able to get that behavior working directly with SFINAE, something like this may work: 请注意,您可以直接使用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

However, I like the expressiveness of the solution based on traits and enable if. 但是,我喜欢基于特征的解决方案的表现力,并启用if。

That's possible, and there are many ways to implement this. 这是可能的,有很多方法可以实现这一点。 All of them should go back on some trait class has_type so that has_type<T>::value is true if the member typedef exists, and false otherwise. 所有这些都应该返回一些特征类has_type以便如果成员typedef存在则has_type<T>::value为true,否则为false。 Let's assume we have this trait class already. 我们假设我们已经有了这个特质类。 Then here's one solution, using C++11 template aliases: 然后这是一个使用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

Now to make the typetrait: 现在来制作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);
};

If you don't have C++11, or if you don't want to rewrite the entire class, you can make the distinction more fine-grained, eg by using std::enable_if , std::conditional , etc. Post a comment if you want some specific examples. 如果您没有C ++ 11,或者您不想重写整个类,则可以使区分更精细,例如使用std::enable_ifstd::conditional等。如果你想要一些具体的例子,请评论。

Using @K-ballo's answer , I wrote the following: 使用@ K-ballo的答案 ,我写了以下内容:

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;
    }
};

Now, the sample code compiles and outputs: 现在,示例代码编译并输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM