当类的构造函数重载为std::initializer_list
时,即使其他构造函数重载看起来更好匹配,此重载也会优先。 Sutter的GotW#1第2部分以及Meyers的Effective Modern C++第7项详细描述了这个问题。
这个问题表现出来的典型例子是大括号初始化std::vector
:
std::vector<int> vec{1, 2};
// Is this a vector with elements {1, 2}, or a vector with a single element 2?
Sutter和Meyers都建议避免使用initializer_list
构造函数重载会导致程序员不明确的类设计。
萨特:
指南:设计类时,请避免提供构造函数 使用initializer_list构造函数模糊地重载,以便 用户不需要使用()来达到这样一个隐藏的构造函数。
迈尔斯:
因此,最好设计你的构造函数 被调用的重载不受客户端是否使用括号或 括号。换句话说,从现在被视为错误的东西中学习 std :: vector接口的设计,并设计你的类 避免它。
但他们都没有描述 vector
应如何设计以避免此问题!
所以这是我的问题:如何设计vector
以避免initializer_list
构造函数重载的歧义(不丢失任何功能)?
答案 0 :(得分:8)
我会采用标准与piecewise_construct
中的pair
或defer_lock
中的unique_lock
采用相同的方法:在构造函数上使用标记:
struct n_copies_of_t { };
constexpr n_copies_of_t n_copies_of{};
template <typename T, typename A = std::allocator<T>>
class vector {
public:
vector(std::initializer_list<T>);
vector(n_copies_of_t, size_type, const T& = T(), const A& = A());
// etc.
};
那样:
std::vector<int> v{10, 20}; // vector of 2 elems
std::vector<int> v2(10, 20); // error - not a valid ctor
std::vector<int> v3(n_copies_of, 10, 20); // 10 elements, all with value 20.
另外,我总是忘记它是10个元素值20或20个元素值10,所以标签有助于澄清。
答案 1 :(得分:1)
避免歧义的一种可能方法是使用静态工厂方法将initializer_list
构造函数与其他构造函数隔离开来。
例如:
template <typename T>
class Container
{
public:
static Container with(size_t count, const T& value)
{
return Container(Tag{}, count, value);
}
Container(std::initializer_list<T> list) {/*...*/}
private:
struct Tag{};
Container(Tag, size_t count, const T& value) {/*...*/}
};
用法:
auto c1 = Container<int>::with(1, 2); // Container with the single element '2'
auto c2 = Container<int>{1, 2}; // Container with the elements {1, 2}
这种静态工厂方法让人想起对象是如何allocated and initialized in Objective-C的。嵌套的Tag
结构用于确保initializer_list
重载不可行。
或者,可以将initializer_list
构造函数更改为静态工厂方法,这样可以保持其他构造函数的重载不变:
template <typename T>
class Container
{
public:
static Container with(std::initializer_list<T> list)
{
return Container(Tag{}, list);
}
Container(size_t count, const T& value) {/*...*/}
private:
struct Tag{};
Container(Tag, std::initializer_list<T> list) {/*...*/}
};
用法:
auto c1 = Container<int>{1, 2}; // Container with the single element '2'
auto c2 = Container<int>::with({1, 2}); // Container with the elements {1, 2}