将可变参数函数模板参数存储到联合向量中的最有效方法?
Most efficient way to store variadic function template arguments into a union vector?
我有一个简单的联合体,其构造函数将其参数存储在相关成员中:
union Data
{
Data(int i) : _i(i) { }
Data(double d) : _d(d) { }
Data(char c) : _c(c) { }
int _i;
double _d;
char _c;
};
然后我有一个可变参数构造函数,它接受这些类型的参数并使用模板递归将它们存储在联合向量中:
template<typename... Ts>
DataStore(Ts... ts)
{
_data.reserve(sizeof...(ts));
store(ts...);
}
template<typename T, typename... Ts>
void store(T t, Ts... ts)
{
_data.push_back(t);
store(ts...);
}
void store()
{
// terminal condition
}
这将导致一系列 vector::push_back
与参数数量匹配的调用。
这是填充联合向量的最efficient/fastest方式吗?
是否有任何技巧(可以特定于 x86-64 / Linux)我可以用来加快速度?
工作示例:
#include <iostream>
#include <vector>
union Data
{
Data(int i) : _i(i) { }
Data(double d) : _d(d) { }
Data(char c) : _c(c) { }
int _i;
double _d;
char _c;
};
struct DataStore
{
template<typename... Ts>
DataStore(Ts... ts)
{
_data.reserve(sizeof...(ts));
store(ts...);
}
template<typename T, typename... Ts>
void store(T t, Ts... ts)
{
_data.push_back(t);
store(ts...);
}
void store()
{
// terminal condition
}
std::vector<Data> _data;
};
int main()
{
DataStore d(1, 2.3, 'c');
std::cout << d._data.size() << '\n'
<< d._data[0]._i << '\n'
<< d._data[1]._d << '\n'
<< d._data[2]._c << '\n';
return 0;
}
怎么样:
template<typename... Ts>
DataStore(Ts... ts)
{
_data = {ts...};
}
这将利用 emplace_back
,因此您也可以直接传递存储在 union 中的基本类型。
可以直接初始化_data
。
template<typename... Ts>
DataStore(Ts... ts) : _data{ts...}
{}
由于您的评论表明您反对使用初始化列表的直接方法,您可以这样做:
template<typename... T>
DataStore(T&&... ts)
{
_data.reserve(sizeof...(ts));
char dummy[] = { (_data.emplace_back(ts), '0')... };
}
这仍然会创建一个数组,但它是一个 char
的数组,可能会被优化掉,并且没有递归,您不需要 store
函数模板的无数实例化。
数组每个元素的初始化程序将参数包中的对象之一插入到向量中(按从左到右的顺序)。在这里使用 emplace_back
允许您将任何类型传递给 DataStore
构造函数,这样您就不会创建 Data
对象,直到它们被插入。这意味着没有为以下对象制作的副本:
DataStore d{ 1, '2', 3.0 };
(堆栈上只是一个未使用的 char[3]
数组。)
如果您决定使用 initializer_list
,您应该立即创建它,而不是在 DataStore 构造函数中创建它:
DataStore(std::initializer_list<Data> list) : _data(list) { }
现在您可以将任意数量的 Data
对象传递给 DataStore
构造函数,因此临时数组是在该构造函数之外创建的,然后直接传递给 vector
成员。使用这个,DataStore d{ Data{1}, Data{'2'}, Data{3.0} }
将构造一个包含三个对象的数组,然后将每个对象复制到向量中。
使用此构造函数您不能执行 DataStore{1, '2', 3.0}
因为编译器不会从不同类型的 braced-init-list 创建 initializer_list<Data>
。
我有一个简单的联合体,其构造函数将其参数存储在相关成员中:
union Data
{
Data(int i) : _i(i) { }
Data(double d) : _d(d) { }
Data(char c) : _c(c) { }
int _i;
double _d;
char _c;
};
然后我有一个可变参数构造函数,它接受这些类型的参数并使用模板递归将它们存储在联合向量中:
template<typename... Ts>
DataStore(Ts... ts)
{
_data.reserve(sizeof...(ts));
store(ts...);
}
template<typename T, typename... Ts>
void store(T t, Ts... ts)
{
_data.push_back(t);
store(ts...);
}
void store()
{
// terminal condition
}
这将导致一系列 vector::push_back
与参数数量匹配的调用。
这是填充联合向量的最efficient/fastest方式吗?
是否有任何技巧(可以特定于 x86-64 / Linux)我可以用来加快速度?
工作示例:
#include <iostream>
#include <vector>
union Data
{
Data(int i) : _i(i) { }
Data(double d) : _d(d) { }
Data(char c) : _c(c) { }
int _i;
double _d;
char _c;
};
struct DataStore
{
template<typename... Ts>
DataStore(Ts... ts)
{
_data.reserve(sizeof...(ts));
store(ts...);
}
template<typename T, typename... Ts>
void store(T t, Ts... ts)
{
_data.push_back(t);
store(ts...);
}
void store()
{
// terminal condition
}
std::vector<Data> _data;
};
int main()
{
DataStore d(1, 2.3, 'c');
std::cout << d._data.size() << '\n'
<< d._data[0]._i << '\n'
<< d._data[1]._d << '\n'
<< d._data[2]._c << '\n';
return 0;
}
怎么样:
template<typename... Ts>
DataStore(Ts... ts)
{
_data = {ts...};
}
这将利用 emplace_back
,因此您也可以直接传递存储在 union 中的基本类型。
可以直接初始化_data
。
template<typename... Ts>
DataStore(Ts... ts) : _data{ts...}
{}
由于您的评论表明您反对使用初始化列表的直接方法,您可以这样做:
template<typename... T>
DataStore(T&&... ts)
{
_data.reserve(sizeof...(ts));
char dummy[] = { (_data.emplace_back(ts), '0')... };
}
这仍然会创建一个数组,但它是一个 char
的数组,可能会被优化掉,并且没有递归,您不需要 store
函数模板的无数实例化。
数组每个元素的初始化程序将参数包中的对象之一插入到向量中(按从左到右的顺序)。在这里使用 emplace_back
允许您将任何类型传递给 DataStore
构造函数,这样您就不会创建 Data
对象,直到它们被插入。这意味着没有为以下对象制作的副本:
DataStore d{ 1, '2', 3.0 };
(堆栈上只是一个未使用的 char[3]
数组。)
如果您决定使用 initializer_list
,您应该立即创建它,而不是在 DataStore 构造函数中创建它:
DataStore(std::initializer_list<Data> list) : _data(list) { }
现在您可以将任意数量的 Data
对象传递给 DataStore
构造函数,因此临时数组是在该构造函数之外创建的,然后直接传递给 vector
成员。使用这个,DataStore d{ Data{1}, Data{'2'}, Data{3.0} }
将构造一个包含三个对象的数组,然后将每个对象复制到向量中。
使用此构造函数您不能执行 DataStore{1, '2', 3.0}
因为编译器不会从不同类型的 braced-init-list 创建 initializer_list<Data>
。