使用 std::initializer_list 构造函数而不产生歧义?
Using std::initializer_list constructor without creating ambiguities?
我有一个名为 Shape
的 class,它可以从任何可迭代对象初始化,还有一个名为 Array
的 class,它只包含一个 Shape
.但是,当我尝试初始化 Array
:
时遇到无法解释的编译错误
class Shape
{
public:
template<typename Iterator>
Shape(Iterator first, Iterator last)
: m_shape(first, last) {}
template <typename Iterable>
Shape(const Iterable& shape)
: Shape(shape.begin(), shape.end()) {}
template<typename T>
Shape(std::initializer_list<T> shape)
: Shape(shape.begin(), shape.end()) {}
private:
std::vector<std::size_t> m_shape;
};
class Array
{
public:
Array(const Shape& shape)
: m_shape(shape) {}
private:
Shape m_shape;
};
int main() {
Shape s{0}; // ok
Array a1({1, 2}); // ok
Array a2({0}); // error
}
编译错误出现在Shape
的第二个构造函数上:
prog.cxx:35:16: required from here
prog.cxx:14:23: error: request for member ‘begin’ in ‘shape’, which is of non-class type ‘const int’
: Shape(shape.begin(), shape.end()) {}
~~~~~~^~~~~
prog.cxx:14:38: error: request for member ‘end’ in ‘shape’, which is of non-class type ‘const int’
: Shape(shape.begin(), shape.end()) {}
~~~~~~^~~
我不明白这里发生了什么。为什么调用 Iterable
构造函数而不是 initializer_list<T>
构造函数?带有 {0}
的 Shape
构造函数和 Array
构造函数有什么区别?
代码格式错误,但不是因为 gcc 声称的原因。当你写:
Array a2({0});
我们使用初始化器 {0}
对 Array
的所有构造函数进行重载解析。
选项 #1 是:
Array(Shape const& );
我们将递归尝试用 {0}
复制初始化 Shape
,由于 std::initializer_list
在期间的优先处理,最终调用 std::initializer_list<int>
构造函数模板列表初始化。
然而,这只是一种选择。选项 #2 是:
Array(Array&& );
隐式移动构造函数。为了检查它是否是候选者,我们看看是否可以用 {0}
初始化 Array
,这基本上会重新开始。在下一层中,我们看看是否可以用 0
初始化 Shape
(因为我们删除了一层),我们 可以 - 这是你的接受 -万物构造器模板。这确实涉及两个用户定义的转换序列,但是that's ok for list-initialization。
所以我们有两个选择:
- 选项 #1:
{0} --> Shape
- 选项 #2:
0 --> Shape --> Array
两者都不比对方好,所以这个称呼是模棱两可的。
简单的解决方法是向构造函数模板添加约束,使其实际上是一个范围。无论如何,这通常是个好习惯,因为您不希望 is_constructible_v<Shape, int>
为真...
我有一个名为 Shape
的 class,它可以从任何可迭代对象初始化,还有一个名为 Array
的 class,它只包含一个 Shape
.但是,当我尝试初始化 Array
:
class Shape
{
public:
template<typename Iterator>
Shape(Iterator first, Iterator last)
: m_shape(first, last) {}
template <typename Iterable>
Shape(const Iterable& shape)
: Shape(shape.begin(), shape.end()) {}
template<typename T>
Shape(std::initializer_list<T> shape)
: Shape(shape.begin(), shape.end()) {}
private:
std::vector<std::size_t> m_shape;
};
class Array
{
public:
Array(const Shape& shape)
: m_shape(shape) {}
private:
Shape m_shape;
};
int main() {
Shape s{0}; // ok
Array a1({1, 2}); // ok
Array a2({0}); // error
}
编译错误出现在Shape
的第二个构造函数上:
prog.cxx:35:16: required from here
prog.cxx:14:23: error: request for member ‘begin’ in ‘shape’, which is of non-class type ‘const int’
: Shape(shape.begin(), shape.end()) {}
~~~~~~^~~~~
prog.cxx:14:38: error: request for member ‘end’ in ‘shape’, which is of non-class type ‘const int’
: Shape(shape.begin(), shape.end()) {}
~~~~~~^~~
我不明白这里发生了什么。为什么调用 Iterable
构造函数而不是 initializer_list<T>
构造函数?带有 {0}
的 Shape
构造函数和 Array
构造函数有什么区别?
代码格式错误,但不是因为 gcc 声称的原因。当你写:
Array a2({0});
我们使用初始化器 {0}
对 Array
的所有构造函数进行重载解析。
选项 #1 是:
Array(Shape const& );
我们将递归尝试用 {0}
复制初始化 Shape
,由于 std::initializer_list
在期间的优先处理,最终调用 std::initializer_list<int>
构造函数模板列表初始化。
然而,这只是一种选择。选项 #2 是:
Array(Array&& );
隐式移动构造函数。为了检查它是否是候选者,我们看看是否可以用 {0}
初始化 Array
,这基本上会重新开始。在下一层中,我们看看是否可以用 0
初始化 Shape
(因为我们删除了一层),我们 可以 - 这是你的接受 -万物构造器模板。这确实涉及两个用户定义的转换序列,但是that's ok for list-initialization。
所以我们有两个选择:
- 选项 #1:
{0} --> Shape
- 选项 #2:
0 --> Shape --> Array
两者都不比对方好,所以这个称呼是模棱两可的。
简单的解决方法是向构造函数模板添加约束,使其实际上是一个范围。无论如何,这通常是个好习惯,因为您不希望 is_constructible_v<Shape, int>
为真...