【问题标题】:What's the point of unnamed non-type template parameters?未命名的非类型模板参数有什么意义?
【发布时间】:2020-01-20 14:01:58
【问题描述】:

根据the reference,非类型模板参数的名称是可选的,即使在分配默认值时也是如此(参见(1)和(2))。因此这些模板结构是有效的:

template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};

我还没有看到访问非类型参数值的可能性。 我的问题是: unnamed/anonymous 非类型模板参数有什么意义?为什么名称是可选的?

【问题讨论】:

  • 为什么要区分非类型模板参数和类型模板参数?
  • 您似乎坚持认为这个问题专门针对非类型模板参数,但答案可能同样适用于类型参数。你熟悉无名类型参数的应用吗?
  • @Jarod42 诚然,我没有过多考虑未命名类型模板参数的原因,但我绝对看不到非类型模板参数的原因。实际上我想知道非类型/类型模板之间是否有区别,但我想在我的问题中更具体。
  • @François 您将更频繁地看到 SFINAE 的未命名类型模板参数。对我来说,使用非类型模板参数执行此操作似乎更复杂或不可能。
  • @n314159 这不是一个很好的参考。原因是默认值不是模板签名的一部分。对于非类型,我们可以通过使用enable_if 指定参数类型来解决此问题,这会导致替换失败或有效的非类型参数,如this answer 所示。

标签: c++ templates generic-programming non-type


【解决方案1】:

首先,我们可以从定义中拆分声明。 所以声明中的名称并没有真正的帮助。和名称可能会在定义中使用

template <int> struct Foo;
template <unsigned long = 42> struct Bar;

template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};

专业化是定义的一种特殊情况。

那么name可以不用,所以我们可以省略它:

template <std::size_t, typename T>
using always_t = T;

template <std::size_t ... Is, typename T>
struct MyArray<std::index_sequence<Is...>, T>
{
    MyArray(always_t<Is, const T&>... v) : /*..*/
};

或用于 SFINAE

template <typename T, std::size_t = T::size()>
struct some_sized_type;

【讨论】:

    【解决方案2】:

    未命名/匿名非类型模板参数有什么意义?

    我能想到专业化:

    template<int = 42>
    struct Foo{
       char x;
    };
    
    template<>
    struct Foo<0> {
       int x;
    };
    
    template<>
    struct Foo<1> {
       long x;
    };
    

    然后:

    Foo<0> a; // x data member is int
    Foo<1> b; // x data member is long
    Foo<7> c; // x data member is char
    Foo<>  d; // x data member is char
    

    【讨论】:

      【解决方案3】:

      哦,您可以访问它们!

      template <int> struct Foo {};
      
      template <int N>
      int get(Foo<N>) {
          return N;
      }
      
      int main() {
          Foo<3> foo;
          return get(foo);
      }
      

      这可能有点做作。但一般来说,对于某些模板,您不想命名它们,那么您不必命名它们很方便。

      【讨论】:

      • 嗯...当然我的意思是无法从模板中访问未命名的参数。如果我在模板实例化之前定义一个常量表达式,确保我总是可以访问该值:-) constexpr int N = 42; Foo&lt;N&gt; f; - 但这并没有多大帮助......
      【解决方案4】:

      未命名的类型和非类型参数还允许您使用模板模板参数来延迟类型实例化。

      以下面函数中的destination_type为例。
      它可以解析为具有模板类型参数和 0 到 N 个模板值参数的任何类型。

      template <template <typename, auto...> typename destination_type, typename TupleType>
      constexpr auto repack(TupleType && tuple_value)
      {
          return [&tuple_value]<std::size_t ... indexes>(std::index_sequence<indexes...>) {
              return destination_type{std::get<indexes>(tuple_value)...};
          }(std::make_index_sequence<std::tuple_size_v<TupleType>>{});
      }
      static_assert(repack<std::array>(std::tuple{1,2,3}) == std::array{1,2,3});
      

      当您需要对参数包进行抽象时,这种机制会派上用场。

      例如,我们不关心Ts... 是包含多个参数的参数包,还是扩展为本身具有多个模板参数的单个类型。

      -> 可以从模板类型参数转换为模板值参数。

      gcl::mp::type_traits::pack_arguments_as_t

      godbolt here 上提供了完整示例。

      template <template <typename ...> class T, typename ... Ts>
      class pack_arguments_as {
          template <template <typename...> class PackType, typename... PackArgs>
          constexpr static auto impl(PackType<PackArgs...>)
          {
              return T<PackArgs...>{};
          }
          template <typename... PackArgs>
          constexpr static auto impl(PackArgs...)
          {
              return T<PackArgs...>{};
          }
          public:
          using type = decltype(impl(std::declval<Ts>()...));
      };
      template <template <typename ...> class T, typename ... Ts>
      using pack_arguments_as_t = typename pack_arguments_as<T, Ts...>::type;
      
      namespace tests
      {
          template <typename... Ts>
          struct pack_type {};
      
          using toto = pack_arguments_as_t<std::tuple, pack_type<int, double, float>>;
          using titi = pack_arguments_as_t<std::tuple, int, double, float>;
      
          static_assert(std::is_same_v<toto, titi>);
          static_assert(std::is_same_v<toto, std::tuple<int, double, float>>);
          static_assert(std::is_same_v<pack_type<int, double, float>, pack_arguments_as_t<pack_type, toto>>);
      }
      

      【讨论】:

        猜你喜欢
        • 2023-01-25
        • 2017-08-31
        • 2013-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-28
        • 2021-03-18
        • 1970-01-01
        相关资源
        最近更新 更多