【问题标题】:Replacement for non-static non-const variable templates替换非静态非常量变量模板
【发布时间】:2021-07-21 08:59:14
【问题描述】:

我刚刚打开了一个问题about this,但不幸的是,正如@super 在他的回答中指出的那样,在函数getArray 中定义arr static 不是我想要的。我想要一个非静态非常量变量模板之类的东西,但到目前为止,c++ 还不支持它。是否有一些解决方法,它与非静态非常量变量模板具有相同的效果。

编辑

如果支持,这将是我期望的语法:

#include <array>


template<typename T_, size_t size_>
struct arg
{
    using T = T_;
    static constexpr size_t size = size_;
};

template<typename... Arugments>
struct Foo
{
    template<typename Argument>
    std::array<typename Argument::T, Argument::size> arr;
    
    template<typename Argument>
    std::array<typename Argument::T, Argument::size>& getArray() // specializations of all args in Arguments should be generated
    {
        return arr<Argument>;
    }
};

int main()
{
    Foo<arg<int, 10>> myFoo;
    myFoo.getArray<arg<int, 10>>()[0] = 1;
    Foo<arg<int, 10>> myFoo2;
    myFoo2.getArray<arg<int, 10>>()[0] = 2; // should not affect the arr of myFoo
}

【问题讨论】:

  • 返回引用是否有特殊原因?您打算如何处理返回类型?
  • 您能否展示您希望代码的外观? (即使它实际上没有编译,因为不支持非静态非常量变量模板)
  • @2b-t 我不得不承认这个例子有点糟糕,我可能过度简化了问题,但我有一个需要静态大小列表的类,而不是 arg 我使用typenames 创建一个在编译时评估的映射,因为这个“映射”的许多成员具有不同的类型。要回答您的问题,没有特殊原因需要返回参考。
  • @GianLaager 如果Arguments 内部有重复,会发生什么情况,这意味着两次完全相同的模板?它会发生还是不会发生?

标签: c++ c++11 templates variables


【解决方案1】:

这个:

struct Foo
{
    template<typename Argument>
    std::array<typename Argument::T, Argument::size> arr;
};

是不可能的,因为您要求编译器创建一个结构,其中包含未知数量的变量(每个用作模板参数的类型都有一个)。它在变量是静态的时候起作用,因为这样变量在内存中不必彼此相邻,编译器可以在请求时创建它们。当它们是非静态的时,编译器必须计算出struct Foo 的内存布局,但它不能这样做,因为它看不到未来或其他源文件。

但是,您希望参数包Arguments 中的每种类型都有一个变量。而你可以做到这一点...用一些奇怪的诡计。每个类型可以有一个基类,每个基类可以有一个变量。这就是像 std::tuplestd::variant 这样的类可能是用你的编译器实现的:

template<typename Argument>
struct FooBase
{
    std::array<typename Argument::T, Argument::size> arr;
};

template<typename... Arguments>
struct Foo : FooBase<Arguments>...
{
    template<typename Argument>
    std::array<typename Argument::T, Argument::size>& getArray()
    {
        return FooBase<Argument>::arr; // will be an error if Argument isn't in Arguments
    }
};

【讨论】:

    【解决方案2】:

    如前所述,您可以添加某种非静态类成员,例如 std::tuplestd::arrays。 Caleth's answer 是一个很好的答案,但需要 C++14:std::get by typename was only introduced in C++14

    在 C++11 中,您必须将 std::get by index找到相应的元组索引结合使用。你必须定义following helper templates:

    template<typename T, typename Tuple>
    struct tuple_element_index_helper;
    
    template<typename T>
    struct tuple_element_index_helper<T, std::tuple<>> {
       static constexpr std::size_t value = 0;
    };
    
    template<typename T, typename... Rest>
    struct tuple_element_index_helper<T, std::tuple<T, Rest...>> {
      static constexpr std::size_t value = 0;
      using RestTuple = std::tuple<Rest...>;
      static_assert(tuple_element_index_helper<T, RestTuple>::value == std::tuple_size<RestTuple>::value, "type appears more than once in tuple");
    };
    
    template<typename T, typename First, typename... Rest>
    struct tuple_element_index_helper<T, std::tuple<First, Rest...>> {
      using RestTuple = std::tuple<Rest...>;
      static constexpr std::size_t value = 1 + tuple_element_index_helper<T, RestTuple>::value;
    };
    
    template<typename T, typename Tuple>
    struct tuple_element_index {
      static constexpr std::size_t value = tuple_element_index_helper<T, Tuple>::value;
      static_assert(value < std::tuple_size<Tuple>::value, "type does not appear in tuple");
    };
    

    那么你可以如下使用它们

    template<typename... Arguments>
    struct Foo {
      private:
        using TupleType = decltype(std::make_tuple(std::array<typename Arguments::T, Arguments::size>() ...));
    
       public:
        TupleType arr;
    
        template<typename Argument>
        std::array<typename Argument::T, Argument::size>& getArray() {
          // equivalent to C++14 get by typename: std::get<std::array<typename Argument::T, Argument::size>>(arr);
          return std::get<tuple_element_index<std::array<typename Argument::T, Argument::size>, TupleType>::value>(arr);
        }
    };
    

    Try it here!


    如果有多个相同类型的模板参数会发生什么?您可以向其中添加static_assert,以保证不允许使用相同类型的两个模板参数。

    【讨论】:

      【解决方案3】:

      您需要在某处对Arguments... 进行包扩展。在 C++14 中

      template <typename... Arugments>
      struct Foo
      {
          template <typename Argument>
          using array_t = std::array<typename Argument::T, Argument::size>;
      
          using arrays_t = std::tuple<array_t<Arguments>...>;
      
          arrays_t arrs;
      
          // C++14 only
          template <typename Argument>
          array_t<Argument> & getArray() { 
              static_assert(contains<Argument, Arguments...>(), "Invalid type");
              return get<array_t<Argument>>(arrs); 
          }
          // and / or (also C++11)
          template <size_t I>
          std::tuple_element_t<I, arrays_t> & getArray() { 
              static_assert(I < sizeof...(Arguments), "Invalid index");
              return get<I>(arrs); 
          }
      };
      

      Live Demo

      【讨论】:

      • 这是一个 C++11 问题,您的答案很好,但需要 C++14(std::get by typename)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-21
      • 2022-01-14
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多