[英]For constructors, how do I choose between variadic-templates vs std::initializer_list?
在当前的c ++ 11状态(比如gcc 4.7.2)中,当我需要一个可以接受变量参数的构造std::initializer_list
时,我应该如何选择使用variadic-template还是std::initializer_list
?
可变参数模板允许您提供不同类型的参数,而std::initializer_list
则使用参数的类型进行模板化。 这意味着列表中所有元素的类型必须相同(或可转换为基础类型,但不允许缩小转换)。 根据您是否需要,您可以选择其中一个。
此外,如果您需要完美转发 ,则可变参数模板通常是默认选项,因为语法形式T&&
可以绑定到左值引用和右值引用,而initializer_list
不能执行类似的类型推导:
struct A
{
// Deduces T& for lvalue references, T for rvalue references, and binds to both
template<typename... Ts>
A(Ts&&...) { }
// This is an rvalue reference to an initializer_list. The above type deduction
// does not apply here
template<typename T>
A(initializer_list<T>&&) { }
};
另请注意,当您使用统一初始化语法(即花括号)时,默认情况下将调用接受initializer_list
的构造函数,即使存在另一个可行的构造函数。 这可能是也可能不是您希望拥有的东西:
struct A
{
A(int i) { }
};
struct B
{
B(int) { }
B(std::initializer_list<A>) { }
};
int main()
{
B b {1}; // Will invoke the constructor accepting initializer_list
}
使用可变参数模板,在编译期间可以知道参数的数量(并且可以通过sizeof...
访问)。 使用std::initializer_list
,参数的数量仅在运行时才知道。 因此,部分决定取决于您何时需要或想知道您有多少参数。
我建议总是选择可变参数模板,尽可能避免使用std::initializer_list
。
这就是我用C ++ 11实现std::vector
:
#include <iostream>
#include <vector>
struct exp_sequence {
template <typename... T>
exp_sequence(T&&...) {}
};
struct from_arglist_t {} from_arglist;
template <typename T>
class my_vector {
std::vector<T> data;
public:
my_vector(int n, T const& e) : data(n, e) {}
template <typename... Args>
my_vector(from_arglist_t, Args&&... args) {
data.reserve(sizeof...(Args));
exp_sequence{(data.push_back(std::forward<Args>(args)),1)...};
}
std::size_t size() { return data.size(); }
};
int main()
{
std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2
std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13
my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13
my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13
my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2
my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2
}
原因如main
,在通用代码中使用initializer_list
可能会导致不同的行为,具体取决于所选择的括号类型。 还可以通过添加这样的构造函数来静默地更改代码。
另一个原因是仅移动类型:
//std::vector<move_only> m1{move_only{}}; // won't compile
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.