当前位置: 首页 > 知识库问答 >
问题:

为一些外部模板类定义部分专用化,并限制模板参数

华瀚漠
2023-03-14

我有一个班级Foo和班级Bar。它们是 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命名空间中实现元组接口:get/tuple_size/tuple_element。但是这些方法应该只对Foo可用,并且派生自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::enableif可以用作模板参数。如果我写:

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

更新:似乎我找到了解决方案。比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个答案

彭宏深
2023-03-14

如果您希望不同的派生组类型有不同的< code>tuple_size类,我的先例解决方案(基于< code>static_assert())不起作用。

我提出了以下基于std::conditionalstd::is_base_of的解决方案

棘手(我想也是丑陋的)部分是结构 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
 }
魏熠彤
2023-03-14

一个好的旧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!
 }
步建茗
2023-03-14

如果您同意使用建议但尚未标准化的语言功能,这似乎在带有 -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");
 类似资料:
  • 这个问题可能太难在标题中的on句子中描述,但这里有一个最小的例子: 当两种类型相同时 当这两种类型是同一类型的类模板的实例化时 天真地说,我们希望第二个部分专门化不会与第一个不明确,因为它感觉“更专门化”,对基础模板上和的推导类型施加更多限制。然而,主要的编译器似乎同意我们的期望是错误的:为什么它不被认为是更专业化的?

  • 也许我累了,但是我被这个简单的局部特化卡住了,它不起作用,因为指定模板参数: 将替换为,或没有帮助。那么这种专门化是否可能呢?

  • 请向我解释选择模板专用化的规则。我举一个例子: 为什么输出为?一般来说,专用类中的默认模板参数会发生什么情况?它会带来一些影响吗?

  • 还尝试在专门化的中进行模板方法专门化: 这一次它编译,但调用原始方法,即 解决方案

  • 我有几个模板参数的模板结构 此结构适用于所有模板,但结果无效的情况除外。我知道,不能实现为void类型,所以我当前的解决方案是使用如下的部分专门化: 这允许执行以下操作: 有没有一种方法可以使编译而不会在C 14标准中进行部分类特化?我可以使用和类型trait组合,但我想找到是否有一种方法: > 模板类方法的特殊化部分显式 模板类方法的实例化