替换非静态非常量变量模板

问题描述

我刚刚打开了一个问题 about this 但不幸的是,正如@super 在他的回答中指出的那样,在函数 arr 中定义 getArray 静态不是我想要的。我想要一个类似非静态非常量变量模板的东西,但直到现在,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
}

解决方法

这个:

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
    }
};
,

您需要在 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

,

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

在 C++11 中,您必须将 std::get 按索引查找相应的元组索引结合使用。您必须定义 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,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,以保证不允许有两个相同类型的模板参数。