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

相關問題