【问题标题】:C++: Can I have non-static member variable templates?C++:我可以有非静态成员变量模板吗?
【发布时间】:2018-10-17 14:29:14
【问题描述】:

我正在尝试编写一些代码,要求我在容器类中有很多 std::arrays。这些数组都有不同的大小(如果重要的话,都是从 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++ 支持变量模板,但似乎只适用于类中的静态成员。有没有可以表现类似的替代方案(最好尽可能少的开销)?

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

提前致谢。

【问题讨论】:

  • 如果你想让你的类成员依赖一个模板参数,那么整个类必须是一个模板。如果个别成员可以有自己的模板参数,我不确定我是否可以想象您将如何指定模板参数。
  • 听起来您可能希望 arr 成为数组的元组。
  • 如果您写出成员,那么拥有像 printOutArr 这样的模板化成员函数很简单,以防您担心成员函数的代码重复...
  • 您可能会编写一个模板类型来创建所需大小的数组的元组或数组。使用生成器序列应该是可行的。
  • 如果您不需要std::array,使用std::vector&lt;std::vector&lt;int&gt;&gt; 来做这件事很简单

标签: c++ templates


【解决方案1】:

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

没有。

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

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>();

【讨论】:

  • 这对我有用。谢谢!我还必须定义一个辅助结构,它会递归地遍历所有数组,但是在看到这个答案以及关于这种模板递归之后,这样做并不难。
【解决方案2】:

我想我可能有一个使用递归模板和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_

【讨论】:

    猜你喜欢
    • 2021-10-13
    • 2017-10-16
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多