C ++:我可以有非静态成员变量模板吗?

时间:2018-10-17 14:29:15

标签: c++ templates

我正在尝试编写一些代码,要求我在容器类中有很多std::array。这些数组的大小各不相同(如果重要的话,它们都是从2-16连续的),并且每个大小都只有一个。我想将它们放在容器类中,并能够使用模板访问它们。

用代码解释可能更容易。我想要这样的东西:

class ContainerClass {

public:
   // I want to declare some number of arrays right here, all of different
   // sizes, ranging from 2-16. I'd like to be able to access them as
   // arr<2> through arr<16>.

   // This code gives a compiler error, understandably. 
   // But this is what I'd think it'd look like.
   template <size_t N> // I also need to find a way to restrict N to 2 through 16.
   std::array<int, N> arr;

   // An example method of how I want to be able to use this.
   template <size_t N>
   void printOutArr() {
       for (int i = 0; i < N; i++) {
           std::cout << arr<N>[i] << std::endl;
       }
   }
};

我希望代码扩展为好像其中只有15个数组(从2到16)。像这样,但是有模板:

class ContainerClass {

public:
    std::array<int, 2> arr2;
    std::array<int, 3> arr3;
    std::array<int, 4> arr4;
    std::array<int, 5> arr5;
   // ... and so on.
};

据我了解,C ++支持变量模板,但似乎仅适用于类中的静态成员。是否有其他选择可以表现类似(最好开销尽可能小)?

如果您需要更多信息,请询问。

谢谢。

2 个答案:

答案 0 :(得分:6)

  

我可以有非静态成员变量模板吗?

否。

但是,您可以使用模板来生成您所描述的成员列表。这是一个使用递归继承的示例:

template<class T, std::size_t base, std::size_t size>
class Stair;

template<class T, std::size_t base>
class Stair<T, base, base> {};

template<class T, std::size_t base, std::size_t size>
class Stair : Stair<T, base, size - 1> {
protected:
    std::array<T, size> arr;
public:
    template<std::size_t s>
    std::array<T, s>& array() {
        return Stair<T, base, s>::arr;
    }
};

int main()
{
    Stair<int, 2, 10> s;
    auto& arr = s.array<9>();

答案 1 :(得分:0)

我认为我可能有一个使用递归模板和std::tuple的解决方案。我使用gcc 7.3.0进行了编译和测试。

这使我感到肮脏,但似乎可以正常工作。

#include <iostream>
#include <array>
#include <tuple>
#include <type_traits>

// Forward declare A since there is a circular dependency between A and Arrays
template <size_t size, size_t start, typename... T>
struct A;

// If size is greater than start define a type that is an A::ArrayTuple from the
// next step down (size - 1) otherwise type is void
template <size_t size, size_t start, typename E, typename... T>
struct Arrays {
    using type = typename std::conditional<(size > start),
        typename A<size-1, start, E, T...>::ArrayTuple,
        void
    >::type;
};

// Use template recursion to begin at size and define std::array<int, size>
// to add to a tuple and continue marching backwards (size--) until size == start
// When size == start take all of the std::arrays and put them into a std::tuple
//
// A<size, start>::ArrayTuple will be a tuple of length (size - start + 1) where
// the first element is std::array<int, start>, the second element is
// std::array<int, start + 1> continuing until std::array<int, size>
template <size_t size, size_t start, typename... T>
struct A {
    using Array = typename std::array<int, size>;

    using ArrayTuple = typename std::conditional<(size == start),
        typename std::tuple<Array, T...>,
        typename Arrays<size, start, Array, T...>::type
    >::type;
};

// This specialization is necessary to avoid infinite template recursion
template <size_t start, typename... T>
struct A<0, start, T...> {
    using Array = void;
    using ArrayTuple = void;
};

template <size_t size, size_t start = 1>
class Container {
public:
    using ArrayTuple = typename A<size, start>::ArrayTuple;

    // Shorthand way to the get type of the Nth element in ArrayTuple
    template <size_t N>
    using TupleElementType = typename
        std::tuple_element<N-start, ArrayTuple>::type;

    ArrayTuple arrays_;

    // Returns a reference to the tuple element that has the type of std::array<int, N>
    template <size_t N>
    TupleElementType<N>& get_array() {
        // Static assertion that the size of the array at the Nth element is equal to N
        static_assert(std::tuple_size< TupleElementType<N> >::value == N);
        return std::get<N-start>(arrays_);
    }

    // Prints all elements of the tuple element that has the type of std::array<int, N>
    template <size_t N>
    void print_array() {
        auto& arr = get_array<N>();
        std::cout << "Array Size: " << arr.size() << std::endl;
        for (int i = 0; i < arr.size(); i++) {
            std::cout << arr[i] << std::endl;
        }
    }
};

int main() {
    // Create a new Container object where the arrays_ member will be
    // a tuple with 15 elements:
    // std::tuple< std::array<int, 2>, std::array<int, 3> ... std::array<int, 16> >
    Container<16,2> ctr;

    auto& arr2 = ctr.get_array<2>();

    arr2[0] = 20;
    arr2[1] = 21;

    //ctr.print_array<1>(); // Compiler error since 1 < the ctr start (2)
    ctr.print_array<2>(); // prints 20 and 21
    ctr.print_array<3>(); // prints 3 zeros
    ctr.print_array<16>(); // prints 16 zeros
    //ctr.print_array<17>(); // Compiler error since 17 > the ctr size (16)

    //int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_

    return 0;
}

如果我取消声明尝试显示int x类型的ctr.arrays_的那一行的注释,则为编译器的输出:

so.cpp: In function ‘int main()’:
so.cpp:90:22: error: cannot convert ‘Container<16, 2>::ArrayTuple {aka std::tuple<std::array<int, 2>, std::array<int, 3>, std::array<int, 4>, std::array<int, 5>, std::array<int, 6>, std::array<int, 7>, std::array<int, 8>, std::array<int, 9>, std::array<int, 10>, std::array<int, 11>, std::array<int, 12>, std::array<int, 13>, std::array<int, 14>, std::array<int, 15>, std::array<int, 16> >}’ to ‘int’ in initialization
     int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_