扩展@ecatmur的答案,我开发了一段代码,可以为任何类型的向量和任何构造函数调用提供非常通用的解决方案。矢量的每个元素的构造函数参数都存储在
tuple
(第页,共页)
&
或
&&
适当时),然后在构建元素时完美转发。每个元素只构造一次,本质上等同于
emplace_back
这种转发甚至允许构建仅移动类型的向量,例如
unique_ptr<?>
.
(
使现代化
,由于RVO,应简单地将其建造到位。然而,不幸的是,元素类型至少需要一个复制构造函数或移动构造函数才能可见,即使优化器实际上跳过了它们。这意味着您可以构建
unique_ptr
,但不是
mutex
.)
auto v2 = make_vector_efficiently<A>(
pack_for_later(1) // 1-arg constructor of A
,pack_for_later(2,"two") // 2-arg constructor of A
,pack_for_later(3) // 1-arg constructor of A
);
上述代码将创建
vector<A>
具有三个元素。在我的示例中,
A
有两个构造函数,其中一个
int,string
作为参数。
pack_for_later
生成
元组
将其参数存储为
&
/
&&
参考文献。然后转换为对象(类型
UniquePointerThatConverts
,在本例中具有所需的转换运算符
operator A()
.
在内部
make_vector_efficiently
,生成这些转换器对象的初始值设定项列表,然后
vector
使用
begin()
和
end()
initializer_list的值。您可能希望这些迭代器必须具有类型
T*
为了构造
vector<T>
,但迭代器指向的类型可以
转换
到
T
.
然后,构造函数使用放置
new
从转换的对象复制到(copy-)构造。但是,感谢RVO,复制不会发生,转换器将有效地执行与
emplace_back
对我们来说。
无论如何,任何反馈都很感谢。最后,将其扩展到除
矢量
.
Full code on Coliru
应适用于任何C++11编译器。
一些更详细的注释和重要功能的副本:
pack_for_稍后
简单地构建
std::tuple
.标准
make_tuple
不够好,因为它忽略了引用。由生成的元组的每个元素
pack_for_稍后
是一个参考(
&
或
&&
视情况而定,取决于原始参数是左值还是右值)
template<typename ...T>
std:: tuple<T&&...> pack_for_later(T&&... args) {
// this function is really just a more
// 'honest' make_tuple - i.e. without any decay
return std:: tuple<T&&...> (std::forward<T>(args)...);
}
下一个
高效制造
是将所有人聚集在一起的功能。它的第一个参数是“Target”类型,即我们希望创建的向量中元素的类型。的集合
tuples
转换为我们的特殊转换器类型
UniquePointerThatConverts<Target>
并且如上所述构建向量。
template<typename Target, typename ...PackOfTuples>
auto make_vector_efficiently(PackOfTuples&&... args)
-> std::vector<Target>
{
auto inits = { UniquePointerThatConverts<Target>(std::forward<PackOfTuples>(args))...};
return std::vector<Target> {std::begin(inits), std::end(inits)};
}
因为
A.
可以有多个构造函数,我们希望能够使用它们中的任意一个,
pack_for_稍后
可以返回许多不同的类型(不要忘记lvalues和rvalues)。但是我们需要一种类型来构建init列表。因此,我们定义了一个合适的接口:
template<typename Target>
struct ConvInterface {
virtual Target convert_to_target_type() const = 0;
virtual ~ConvInterface() {}
};
因此,每个元组通过以下方式转换为实现此接口的对象:
make_Conv_from_tuple
。它实际上返回一个
唯一_ptr
然后将其存储在
转换的唯一指针
它具有实际的转换运算符。正是这种类型存储在init列表中,用于初始化向量。
template<typename Target>
struct UniquePointerThatConverts {
std:: unique_ptr<ConvInterface<Target>> p; // A pointer to an object
// that implements the desired interface, i.e.
// something that can convert to the desired
// type (Target).
template<typename Tuple>
UniquePointerThatConverts(Tuple&& p_)
: p ( make_Conv_from_tuple<Target>(std:: move(p_)) )
{
//cout << __PRETTY_FUNCTION__ << endl;
}
operator Target () const {
return p->convert_to_target_type();
}
};
当然,还有从包中构造的实际转换运算符。
template<typename Target, typename ...T>
struct Conv : public ConvInterface<Target> {
std::tuple<T...> the_pack_of_constructor_args;
Conv(std::tuple<T...> &&t) : the_pack_of_constructor_args(std:: move(t)) {}
Target convert_to_target_type () const override {
using idx = typename make_my_index_sequence<sizeof...(T)> :: type;
return foo(idx{});
}
template<size_t ...i>
Target foo(my_index_sequence<i...>) const {
// This next line is the main line, constructs
// something of the Target type (see the return
// type here) by expanding the tuple.
return {
std:: forward
< typename std:: tuple_element < i , std::tuple<T...> > :: type >
(std:: get<i>(the_pack_of_constructor_args))
...
};
}
};