我创建了一个可复制的最小示例:
class Vector {
private:
size_t sz;
double* elem;
public:
Vector(size_t s) : sz{s}, elem{new double[s]} {}
Vector(std::initializer_list<double> lst) : sz{lst.size()}, elem{new double[lst.size()]} {
std::copy(lst.begin(), lst.end(), elem);
}
size_t size() const {return sz;}
double& operator[](size_t i) {
if (i < 0 || i >= sz)
throw std::out_of_range("Vector::operator[](size_t): index out of range");
return elem[i];
}
};
int main() {
Vector v1{3};
Vector v2{v1.size()};
std::cout << v2[0] << '\n';
std::cout << v2[1] << '\n';
}
当我编译时,我得到警告:problem.cpp:25:22: warning: narrowing conversion of ‘v1.Vector::size()’ from ‘size_t’ {aka ‘long unsigned int’} to ‘double’
.根据this问题(感谢@Joel的参考),这是因为列表初始化器构造函数优先。当我运行代码时,我可以看到这一点,因为我得到了错误:
terminate called after throwing an instance of 'std::out_of_range'
what(): Vector::operator[](size_t): index out of range
显然,v1
和v2
都是作为单个元素向量构造的,因此调用v2[1]
没有意义。虽然我已经发现了问题,但我被提到的问题并没有完全解决它。我的问题是:有没有推荐的方法来克服这种行为?()
初始化是正确的吗?然而,这在某种程度上违背了统一初始化的目的。或者我应该将一些构造函数设置为explicit
?或者有其他标准方法来规避此功能?
1条答案
按热度按时间57hvy0tb1#
在这方面,统一初始化从根本上被打破。即使你修复了你的自定义容器,标准容器也会永远存在这个问题。
IMO,在任何地方使用它都没有好处。如果你担心隐式转换,
-Wconversion
(或你的编译器的等价物)将比统一初始化更好地捕捉它们。但是如果你想在你的类中使用它,你可以使用标记分派(也就是一个额外的未使用的空结构参数):
那么
Vector v1{3};
将产生一个单元素向量,而Vector v1{WithSize{}, 3};
将给你给予3个零。或者我应该将一些构造函数设置为显式的?
std::vector
的(size_t)
构造函数是explicit
,这至少使= {...}
语法在这方面是安全的(它总是调用initializer_list
构造函数,而不会返回到size构造函数)。但这是另一回事,对你的问题没有帮助。如果按照建议使用标记分派,则没有必要添加
explicit
。