首页 文章

为模板参数限制定义某些外部模板类的部分特化

提问于
浏览
3

我有一个班Foo和 class 酒吧 . 它们是std :: array的包装器 . 它们都有一些派生类 .

template<typename T, std::size_t N>
struct Foo {
    std::array<T, N> data;
};

template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};

template<typename T, std::size_t N>
struct Bar {
    std::array<T, N> data;
};

template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};

我想在std命名空间中实现tuple-interface:get / tuple_size / tuple_element . 但是这些方法对于Foo应该是 only ,并且可以从Foo类派生 .

namespace std {
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
class tuple_size<T<TArg, NArg>>
  : public integral_constant<std::size_t, NArg>
  {
  };

template<std::size_t I, template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
struct tuple_element<I, T<TArg, NArg>>
  {
  using type = TArg;
  };
} // namespace std

这有效,但也适用于Bar和Bar派生类 .

我想用std :: enable_if和std :: is_base_of . 类的std :: enable_if可以用作模板参数 . 如果我写:

template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg,
    typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, int>::type = 0>
class tuple_size<T<TArg, NArg>>
  : public integral_constant<std::size_t, NArg>
  {
  };

它导致编译错误:默认模板参数可能不会用于部分特化 .

是否有可能禁止使用类似于元组的接口来与Foo类无关?

示例:http://rextester.com/JEXJ27486

Update: 似乎我找到了解决方案 . 比static_assert更灵活 . 如果在部分特化中无法使用默认模板参数 - 请添加具有完整模板特化的其他类 .

template<typename T, typename TArg, std::size_t NArg, typename = void>
struct tuple_resolver;

template<typename T, typename TArg, std::size_t NArg>
struct tuple_resolver<T, TArg, NArg,
    typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T>::value>::type>
    : public integral_constant<std::size_t, NArg>
{
    using type = TArg;
};

template<template<typename, std::size_t> class T,
    typename TArg,
    std::size_t NArg>
class tuple_size<T<TArg, NArg>>
  : public tuple_resolver<T<TArg, NArg>, TArg, NArg>
  {
  };

示例:http://rextester.com/KTDXNJ90374

3 回答

  • 2

    如果你可以使用提议但尚未标准化的语言功能,这似乎可以在带有 -fconcepts 标志的gcc 6.1下工作:

    template <typename Base, typename Derived>
    concept bool BaseOf = std::is_base_of<Base, Derived>::value;
    
    namespace std {
      template <template<typename,std::size_t> class Tmpl, typename T, std::size_t N>
      requires BaseOf<Foo<T, N>, Tmpl<T, N>>
        class tuple_size<Tmpl<T, N>>
        : public std::integral_constant<std::size_t, N>
        { };
    };
    
    // tests
    static_assert(std::tuple_size<FooDerived<int, 5>>::value == 5,
                  "FooDerived");
    static_assert(std::tuple_size<std::array<int, 5>>::value == 5,
                  "std::array");
    
    template <typename T>
    concept bool SizedTuple = requires {
      { std::tuple_size<T>::value } -> std::size_t
    };
    static_assert(!SizedTuple<BarDerived<int, 5>>, "BarDerived");
    
  • 0

    一个好老 static_assert() 怎么样?

    #include <array>
    #include <iostream>
    #include <type_traits>
    
    template<typename T, std::size_t N>
    struct Foo {
        std::array<T, N> data;
    };
    
    template<typename T, std::size_t N>
    struct FooDerived : Foo <T, N> {};
    
    template<typename T, std::size_t N>
    struct Bar {
        std::array<T, N> data;
    };
    
    template<typename T, std::size_t N>
    struct BarDerived : Bar <T, N> {};
    
    template <typename>
    class tuple_size;
    
    
    template <template <typename, std::size_t> class T,
              typename TArg,
              std::size_t NArg>
    class tuple_size<T<TArg, NArg>>
     {
       static_assert(std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value,
                     "bad tuple_size class");
     };
    
    
    int main()
     {
       tuple_size<Foo<int, 12>>          ts1;   // compile
       tuple_size<FooDerived<long, 21>>  ts2;   // compile
       //tuple_size<Bar<int, 11>>          ts3;   // error!
       //tuple_size<BarDerived<long, 22>>  ts4;   // error!
     }
    
  • 3

    如果您想为不同的派生组类型使用不同的 tuple_size 类,我的先前解决方案(基于 static_assert() )不起作用 .

    我建议基于 std::conditionalstd::is_base_of 的以下解决方案

    棘手(而且很丑陋,我猜)部分是 struct baseType ,如果可能的话,给定一个类型,检测一个基数(在 FooBar 之间) .

    #include <array>
    #include <iostream>
    #include <type_traits>
    
    template<typename T, std::size_t N>
    struct Foo {
        std::array<T, N> data;
    };
    
    template<typename T, std::size_t N>
    struct FooDerived : Foo <T, N> {};
    
    template<typename T, std::size_t N>
    struct Bar {
        std::array<T, N> data;
    };
    
    template<typename T, std::size_t N>
    struct BarDerived : Bar <T, N> {};
    
    
    template <typename>
    struct baseType;
    
    template <template <typename, std::size_t> class T,
              typename TArg,
              std::size_t NArg>
    struct baseType<T<TArg, NArg>>
     {
       using derT = T<TArg, NArg>;
       using bas1 = Foo<TArg, NArg>;
       using bas2 = Bar<TArg, NArg>;
       using type = typename std::conditional<
                       std::is_base_of<bas1, derT>::value, bas1,
                          typename std::conditional<
                             std::is_base_of<bas2, derT>::value, bas2,
                                void>::type>::type;
     };
    
    template <typename T, typename = typename baseType<T>::type>
    class tuple_size;
    
    template <template <typename, std::size_t> class T,
              typename TArg,
              std::size_t NArg>
    class tuple_size<T<TArg, NArg>, Foo<TArg, NArg>>
     { public: static constexpr std::size_t size {NArg}; };
    
    template <template <typename, std::size_t> class T,
              typename TArg,
              std::size_t NArg>
    class tuple_size<T<TArg, NArg>, Bar<TArg, NArg>>
     { public: static constexpr std::size_t size { NArg << 1 }; };
    
    int main()
     {
       std::cout << "size of Foo<int, 12>:         "
          << tuple_size<Foo<int, 12>>::size << std::endl; //         print 12
       std::cout << "size of FooDerived<long, 11>: "
          << tuple_size<FooDerived<long, 11>>::size << std::endl; // print 11
       std::cout << "size of Bar<int, 12>:         "
          << tuple_size<Bar<int, 12>>::size << std::endl; //         print 24
       std::cout << "size of BarDerived<long, 11>: "
          << tuple_size<BarDerived<long, 11>>::size << std::endl; // print 22
       //std::cout << "size of std::array<long, 10>: "              // compiler 
       //   << tuple_size<std::array<long, 10>>::size << std::endl; // error
     }
    

相关问题