是否有任何语法可用于在模板参数包的参数之间分配非类型参数包,从而期望非类型包(大小不同)?由于这很令人困惑,我相信可以举一个例子来阐明我的意思:https://godbolt.org/z/FaEGTV
template <typename T, int... I> struct Vec { };
struct A
{
template<template<typename, int...> typename... Container,
typename... Ts, int... Is>
A(Container<Ts,Is...>... );
};
A a(Vec<int, 0>{}, Vec<double, 0>{}); // ok
A b(Vec<int, 0, 1>{}, Vec<double, 0, 1>{}); // ok
A c(Vec<int, 0>{}, Vec<double, 0, 1>{}); // error
我希望标记为// error
的行使用与我相似的语法。显然,如果我编写一个特殊的构造函数来处理这种情况,它将很好地工作。但是,我希望此方法适用于任何数量的容器,而不必在所有可能的情况下都明确说明它。例如,如果我有2个容器a,b
,分别具有索引集{0,1,2}
和{0,1,2,3}
,则扩展应类似于A(a[0],a[1],a[2], b[0],b[1],b[2],b[3])
。
我知道我可以递归地执行此操作,一次解压缩一个容器,然后递归地委托给构造函数,这些构造函数期望一开始只包含平面元素。我的问题是,以更优雅,更有效,更省力的方式这样做是否可行。
答案 0 :(得分:1)
例如,如果我有2个容器
a,b
,具有索引集{0,1,2}
和{0,1,2,3}
,则扩展应该类似于A(a[0],a[1],a[2], b[0],b[1],b[2],b[3])
。我知道我可以递归地执行此操作,一次解压缩一个容器,然后递归地委托给构造函数,这些构造函数期望一开始只包含平面元素。我的问题是,以更优雅,更有效,更省力的方式这样做是否可行。
您是否接受扩展为std::tuple
和a[0],a[1],a[2], b[0],b[1],b[2],b[3]
的解决方案?
在这种情况下,您可以遵循Igor的建议,将容器中的值解包,然后将它们重新打包为元组,然后使用std::tuple_cat()
来连接元组。
我的意思是...给出了如下的容器/元组转换器
template <template<typename, std::size_t...> typename C,
typename T, std::size_t... Is>
auto getTpl (C<T, Is...> const & v)
{ return std::make_tuple(v.data[Is]...); }
您可以开始编写构造函数,如下所示调用委托构造函数
template <typename ... Ts>
A (Ts const & ... ts) : A{ std::tuple_cat( getTpl(ts)... ) }
{ }
最后的构造函数是
template <typename ... Ts>
A (std::tuple<Ts...> const & tpl)
{ /* do something with values inside tpl */ }
以下是完整的编译示例
#include <iostream>
#include <string>
#include <tuple>
template <typename T, std::size_t ... Is>
struct Vec
{
T data [sizeof...(Is)] = { Is... };
T const & operator[] (std::size_t i) const
{ return data[i]; }
T & operator[] (std::size_t i)
{ return data[i]; }
};
template <template<typename, std::size_t...> typename C,
typename T, std::size_t... Is>
auto getTpl (C<T, Is...> const & v)
{ return std::make_tuple(v.data[Is]...); }
struct A
{
template <typename ... Ts>
A (std::tuple<Ts...> const & tpl)
{ /* do something with values inside tpl */ }
template <typename ... Ts>
A (Ts const & ... ts) : A{ std::tuple_cat( getTpl(ts)... ) }
{ }
};
int main ()
{
A a(Vec<int, 0>{}, Vec<double, 0>{}); // ok
A b(Vec<int, 0, 1>{}, Vec<double, 0, 1>{}); // ok
A c(Vec<int, 0>{}, Vec<double, 0, 1>{}); // ok, now
}
答案 1 :(得分:1)
我会简单地做:
IntelliJ IDEA 2019.2.3
使用template <typename T>
struct is_container_type : std::false_type{};
template<template<typename, size_t...> typename C, typename T, size_t... Is>
struct is_container_type<C<T, Is...>> : std::true_type
{
// Potential `using` to retrieve C, T, Is...
};
struct A
{
template<typename... Cs, REQUIRES(is_container_type<Cs>::value && ...)>
A(Cs... cs)
{
((void)(std::cout << cs << "\n"), ...);
}
};
(多个元组的std::apply
版本)(您可以找到there),您可以这样做:
multi_apply
答案 2 :(得分:0)
我设法产生了一个效率不高的解决方案,该方案在每个递归步骤的第一个参数中都构建了一个更大的向量,然后还有一个接受单个向量并将其扩展的构造函数。显然,这是不理想的,因为在最坏的情况下,会创建N个向量并包含元素N^2/2
的总数。这是一个示例实现,说明我做了什么:https://coliru.stacked-crooked.com/a/1f41f3793846cdb1
我尝试了一个不同的版本,其中没有构造新对象,但是由于某种原因,编译器未能设法推断出正确的构造函数(我认为这是由于多次扩展引起的)-此版本位于{{1 }}在上面链接的示例中进行定义。