我正在考虑以下问题:
让我们为以下列方式定义的合并数组提供合并函数:
// input is (const void*, size_t, const void*, size_t,...)
template<typename...ARGS>
MyArray Concatenation(ARGS...args)
让我们有几个具有静态属性的结构
struct A { static void* DATA; static size_t SIZE; };
struct B { static void* DATA; static size_t SIZE; };
struct C { static void* DATA; static size_t SIZE; };
我想要一个方法:
template<typename...ARGS>
MyArray AutoConcatenation();
其中ARGS应该与提到的静态接口结构化。 以下方法应具有相同的输出:
AutoConcatenation<A, B, C>();
Concatenation(A::DATA, A::SIZE, B::DATA, B::SIZE, C::DATA, C::SIZE);
我的问题是如何实现参数包扩展。
我试过了:
// not working
template<typename...ARGS>
MyArray AutoConcatenation()
{
return Concatenation((ARGS::DATA, ARGS::SIZE)...);
}
扩展怎么样
ARGS::DATA... // Correct expansion of pointers
ARGS::SIZE... // Correct expansion of sizes
(ARGS::DATA, ARGS::SIZE)... // Seems to be expansion of sizes
只需咨询顾问。我正在寻找 AutoConcatenation 方法的实现,不是为了重新声明,也不是为了重新声明以前的代码,谢谢。
答案 0 :(得分:1)
使用std::tuple
的惰性解决方案:
DATA
和SIZE
元组,std::tuple_cat
,Concatenation
中的索引列表,将生成的元组元素应用于std::index_sequence
。在以下代码中,测试工具比实际解决方案更长:
#include <cstddef>
#include <tuple>
#include <utility>
#include <iostream>
#include <typeinfo>
#include <type_traits>
struct MyArray { };
template<class... ARGS> MyArray Concatenation(ARGS... args)
{
// Just some dummy code for testing.
using arr = int[];
(void)arr{(std::cout << typeid(args).name() << ' ' << args << '\n' , 0)...};
return {};
}
struct A { static void* DATA; static std::size_t SIZE; };
struct B { static void* DATA; static std::size_t SIZE; };
struct C { static void* DATA; static std::size_t SIZE; };
// Also needed for testing.
void* A::DATA;
std::size_t A::SIZE;
void* B::DATA;
std::size_t B::SIZE;
void* C::DATA;
std::size_t C::SIZE;
// The useful stuff starts here.
template<class T, std::size_t... Is> MyArray concat_hlp_2(const T& tup, std::index_sequence<Is...>)
{
return Concatenation(std::get<Is>(tup)...);
}
template<class T> MyArray concat_hlp_1(const T& tup)
{
return concat_hlp_2(tup, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp_1(std::tuple_cat(std::make_tuple(ARGS::DATA, ARGS::SIZE)...));
}
int main()
{
AutoConcatenation<A, B, C>();
}
如果你想避免std::tuple
和std::tuple_cat
(在编译时间方面可能很重),这里可以使用索引到数组中。
测试代码保持不变,这只是多汁的东西:
template<std::size_t Size> const void* select(std::false_type, std::size_t idx,
const void* (& arr_data)[Size], std::size_t (&)[Size])
{
return arr_data[idx];
}
template<std::size_t Size> std::size_t select(std::true_type, std::size_t idx,
const void* (&)[Size], std::size_t (& arr_size)[Size])
{
return arr_size[idx];
}
template<std::size_t... Is> MyArray concat_hlp(std::index_sequence<Is...>,
const void* (&& arr_data)[sizeof...(Is) / 2], std::size_t (&& arr_size)[sizeof...(Is) / 2])
{
return Concatenation(select(std::bool_constant<Is % 2>{}, Is / 2, arr_data, arr_size)...);
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp(std::make_index_sequence<sizeof...(ARGS) * 2>{}, {ARGS::DATA...}, {ARGS::SIZE...});
}
再一个索引序列是原始参数包大小的两倍,但我们构建了DATA
和SIZE
的单独数组,然后使用标记调度从一个或另一个中选择元素,具体取决于当前指数的平价。
这可能看起来不像以前的代码那么好,但它并不涉及任何模板递归(据我所知,现代编译器中使用编译器内在函数实现std::make_index_sequence
)并减少了模板实例化的数量,因此编译速度应该更快。
通过使用指向静态成员的指针数组,可以使select
帮助器成为constexpr
,但实际上这是不必要的。我已经测试了MSVC 2015 U2,Clang 3.8.0和GCC 6.1.0,他们都对此进行了优化,直接调用了Concatenation
(就像基于元组的解决方案一样)。
答案 1 :(得分:0)
这是可能的解决方案:
enum Delimiters { Delimiter };
const void* findData(size_t count) { return nullptr; }
template<typename...ARGS>
const void* findData(size_t count, size_t, ARGS...args)
{
return findData(count, args...);
}
template<typename...ARGS>
const void* findData(size_t count, const void* data, ARGS...args)
{
return count ? findData(count - 1, args...) : data;
}
template<typename...ARGS>
MyArray reordered(size_t count, Delimiters, ARGS...args)
{
return Concatenate(args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, const void* size, ARGS...args)
{
return reordered(count, args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, size_t size, ARGS...args)
{
return reordered(count + 1, args..., findData(count, args...), size);
}
template<typename...ARGS>
MyArray AutoConcatenate()
{
return reordered(0, ARGS::LAYOUT_SIZE..., ARGS::LAYOUT..., Delimiter);
}
如果您了解更优雅的方式,请告诉我。
修改强>
一个更优雅的方法是将函数参数 count 保持为模板参数...
答案 2 :(得分:0)
我认为以下更优雅,它说明了常见的递归解包模式。最后,它不会对内存布局执行任何巫术,并且尝试在C ++泛型编程中使用惯用语。
#include <iostream>
#include <string>
using namespace std;
// Handle zero arguments.
template <typename T = string>
T concat_helper() { return T(); }
// Handle one pair.
template <typename T = string>
T concat_helper(const T &first, size_t flen) { return first; }
// Handle two or more pairs. Demonstrates the recursive unpacking pattern
// (very common with variadic arguments).
template <typename T = string, typename ...ARGS>
T concat_helper(const T &first, size_t flen,
const T &second, size_t slen,
ARGS ... rest) {
// Your concatenation code goes here. We're assuming we're
// working with std::string, or anything that has method length() and
// substr(), with obvious behavior, and supports the + operator.
T concatenated = first.substr(0, flen) + second.substr(0, slen);
return concat_helper<T>(concatenated, concatenated.length(), rest...);
}
template <typename T, typename ...ARGS>
T Concatenate(ARGS...args) { return concat_helper<T>(args...); }
template <typename T>
struct pack {
T data;
size_t dlen;
};
template <typename T>
T AutoConcatenate_helper() { return T(); }
template <typename T>
T AutoConcatenate_helper(const pack<T> *packet) {
return packet->data;
}
template <typename T, typename ...ARGS>
T AutoConcatenate_helper(const pack<T> *first, const pack<T> *second,
ARGS...rest) {
T concatenated = Concatenate<T>(first->data, first->dlen,
second->data, second->dlen);
pack<T> newPack;
newPack.data = concatenated;
newPack.dlen = concatenated.length();
return AutoConcatenate_helper<T>(&newPack, rest...);
}
template <typename T, typename ...ARGS>
T AutoConcatenate(ARGS...args) {
return AutoConcatenate_helper<T>(args...);
}
int main() {
pack<string> first;
pack<string> second;
pack<string> third;
pack<string> last;
first.data = "Hello";
first.dlen = first.data.length();
second.data = ", ";
second.dlen = second.data.length();
third.data = "World";
third.dlen = third.data.length();
last.data = "!";
last.dlen = last.data.length();
cout << AutoConcatenate<string>(&first, &second, &third, &last) << endl;
return 0;
}
我们既不会根据需要更改Concatenate<>()
的声明,也不会更改AutoConcatenate<>()
的声明。我们可以像我们一样自由地实现AutoConcatenate<>()
,并且我们假设有一些Concatenate<>()
的实现(我们为一个工作示例提供了一个简单的实现)。