问题描述
我刚刚打开了一个问题 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::tuple
和 std::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);
}
};
,
如前所述,您可以添加某种非静态类成员,例如 std::tuple
的 std::array
。 Caleth'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);
}
};
如果有多个相同类型的模板参数会发生什么?您可以向其中添加一个 static_assert
,以保证不允许有两个相同类型的模板参数。