4

我有一个叫做Shape的类,它可以从任何可迭代的类中被初始化,并且一个名为Array的类只包含一个Shape。但是,我得到一个编译错误,当我尝试初始化Array我无法解释:使用std :: initializer_list构造函数而不产生歧义?

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>构造函数? Shape构造函数与{0}Array构造函数有什么区别?

+0

我无法复制;你的代码在我的g ++ 6.3.0和我的clang ++ 3.8.1(我的意思是......如果你更正了'NDShape',对于第二个构造函数,在'Shape'中编译得很好)。你正在使用哪种编译器? – max66

+0

你说得对,对不起。我简化了代码太多。更新的代码现在应该会给你一个错误。谢谢! – AstrOne

+0

现在我有一个错误,但它与您所报告的完全不同;你可以确认“没有匹配函数调用'cbegin(const int&)[...]”错误吗? – max66

回答

3

代码格式不正确,但不是gcc声称它的原因。当你写:

Array a2({0}); 

我们确实在使用初始化{0}中的Array所有构造函数重载解析。

选项#1:

Array(Shape const&); 

上,我们将递归到试图复制初始化Shape{0}这最终调用std::initializer_list<int>构造模板由于列表初始化期间优惠待遇std::initializer_list

但是,这只是一个选项。选项#2是:

Array(Array&&); 

隐式移动构造函数。要检查这是否是候选人,我们看看我们是否可以初始化Array{0},基本上重新开始。在下一层中,我们看看我们是否可以用0初始化Shape(因为我们删除了一层),并且我们可以可以 - 这是您的可接受所有东西的构造器模板。这确实涉及两个用户定义的转换序列,但是that's ok for list-initialization

因此,我们有两种选择:

  • 选项#1:{0} --> Shape
  • 选项#2:0 --> Shape --> Array

也不是比其他更好的,所以调用的是模糊的。


简单的解决方法是向您的构造函数模板添加一个约束,使其实际上是一个范围。无论如何,这通常是很好的做法,因为你不希望is_constructible_v<Shape, int>为真...

+0

+1。有趣的是,通过写'显式数组(const Shape&)'来抑制转换'Shape' - >'Array',可以移除选项#2。 Clang-5.0.0(开心)和GCC 7.2(抱怨)[不同意是否解决了这个问题](https://godbolt.org/g/6ty5e7)。提交错误报告有意义吗?通过声明相应的构造函数为'explicit' [按预期工作](https://godbolt.org/g/nEB2vo)来抑制转换'int' - >'Shape'(并且比添加约束更简单)。您可能希望添加单参数构造函数应始终被视为“显式”候选项。 – Julius

+0

@Julius是的,我认为这是一个海湾合作委员会的错误。但是'explicit'并不是真的够用,因为它不像'Shape s(0);'也是有效的 - 无论如何你都需要约束。 – Barry

相关问题