【问题标题】:How to specialize a class for an inner type of a template?如何为模板的内部类型专门化一个类?
【发布时间】:2017-01-19 21:38:57
【问题描述】:

我有一个作为类型特征的类,返回某个条件是否为真。它旨在将类标记为支持特定功能。

template <typename T> struct Check : std::false_type { };

我有一个包含内部类的模板类:

template <unsigned N>
struct Kitty
{
  struct Purr;
};

我想将内部类Purr 标记为支持Check 表示的功能。换句话说,我想让Check&lt;Kitty&lt;123&gt;::Purr&gt;::valuetrue。我尝试执行以下操作,但出现错误:

template <unsigned X>
struct Check<typename Kitty<X>::Purr> : std::true_type { };

错误:模板参数在偏特化中不可推导出:

是否有可能做到这一点,或者它是 C++ 的一个限制,你不能专注于内部模板类成员?

【问题讨论】:

  • 不,错误是明确说明问题所在。这是一个限制,而不是您的建议。像struct Check&lt;typename Kitty&lt;0&gt;::Purr&gt; : true_type { }; 这样的东西很好。
  • 很明显,您的意思是要涵盖Kitty::Purr 的每个专业。但是考虑一下如果Kitty 的特化没有Purr (template&lt;&gt; struct Kitty&lt;0&gt; {};) 会发生什么。该语言通过说您根本无法做到这一点来解决这个问题。在这种情况下,最好简单地忽略该专业化,或者以某种方式说每个专业化都必须符合该专业化,但是唉。您可能会做的是创建一个基类struct KittyBase { struct Purr; }; 并让Kitty 继承它。需要Purr 才能访问N?将Purr 设为基础模板。

标签: c++ templates partial-specialization


【解决方案1】:

正如我在评论中所概述的,可以通过使用基类(我将其称为KittyBase)将其作为推断的上下文。使用基类实际上对于模板来说很常见,以避免为每个新实例重复不必要的代码。我们可以用同样的技术得到Purr,而不需要推导出N

但是,只需将Purr 放在基类中即可删除其对N 的访问权限。幸运的是,即使将 Purr 本身作为模板,这仍然可以是非推断上下文:Live example

#include <type_traits>

template <typename T> struct Check : std::false_type { };

struct KittyBase 
{    
    template<unsigned N> // Template if Purr needs N.
    struct Purr;

protected:
    ~KittyBase() = default; // Protects against invalid polymorphism.
};

template <unsigned N>
struct Kitty : private KittyBase
{
    using Purr = KittyBase::Purr<N>; // Convenience if Purr needs N.
    Purr* meow;
};

template <unsigned X>
struct Check<typename KittyBase::Purr<X>> : std::true_type { };

static_assert(not Check<int>{});
static_assert(Check<Kitty<123>::Purr>{});
static_assert(Check<Kitty<0>::Purr>{});

int main() {}

如果您愿意,您甚至可以将KittyBase::Purr 设为私有并使用template&lt;typename T&gt; friend struct Check; 授予对特征的访问权限。不幸的是,我不知道您是否可以将其限制为仅针对该特征的某些特化。

【讨论】:

    【解决方案2】:

    This answer 有一种有趣的方法来使用 SFINAE 查找类型是否存在。

    适用于检查类型 T::Purr 是否存在,它允许您编写类型特征而不会出现有问题的特化。

    #include <type_traits>
    
    template <unsigned T>
    struct Kitty
    {
        struct Purr{};
    };
    
    // A specialization without Purr, to test
    template <>
    struct Kitty<5>{ };
    
    // has_purr is taken and adapted from https://stackoverflow.com/a/10722840/7359094
    template<typename T>
    struct has_purr
    {   
        template <typename A> 
        static std::true_type has_dtor(decltype(std::declval<typename A::Purr>().~Purr())*);
    
        template<typename A>
        static std::false_type has_dtor(...);
    
        typedef decltype(has_dtor<T>(0)) type;
    
        static constexpr bool value = type::value;
    };
    
    // Check if a type is an instance of Kitty<T>
    template<typename T>
    struct is_kitty : std::false_type {};
    template<unsigned T>
    struct is_kitty<Kitty<T>> : std::true_type {};
    
    template <typename T> 
    struct Check : std::bool_constant< is_kitty<T>::value && has_purr<T>::value> {};
    
    static_assert( Check<int>::value == false, "int doesn't have purr" );
    static_assert( Check<Kitty<0>>::value == true, "Kitty<0> has purr" );
    static_assert( Check<Kitty<5>>::value == false, "Kitty<5> doesn't has purr" );
    

    【讨论】:

      猜你喜欢
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-17
      相关资源
      最近更新 更多