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()) {} 
           ~~~~~~^~~ 

私は「ドンここで起こっていることを理解する。 initializer_list<T>コンストラクタの代わりにIterableコンストラクタが呼び出されるのはなぜですか? {0}ArrayコンストラクタのShapeコンストラクタの違いは何ですか?

+0

私は再現できません。私のg ++​​ 6.3.0と私のclang ++ 3.8.1とで、あなたのコードはうまくコンパイルされます( 's5'行にエラーはありません)。第2のコンストラクタの' NDShape'を 'Shape' )。どのコンパイラを使用していますか? – max66

+0

あなたは正しいです、申し訳ありません。コードをあまりにも単純化しました。更新されたコードでエラーが発生するはずです。ありがとう! – AstrOne

+0

今私はエラーがありますが、あなたが報告したものとはまったく異なります。あなたは "cbegin(const int&)[...]"呼び出しのための "no matching function"を確認できますか? – max66

答えて

3

コードは不正ですが、gccが主張している理由ではありません。あなたが書くとき:

Array a2({0}); 

を私たちは、初期化子{0}を使用してArrayのすべてのコンストラクタオーバーオーバーロードの解決を行います。

オプション#1は次のようになります。私たちはコピー初期化するために、リストの初期化時にstd::initializer_listの優遇によるstd::initializer_list<int>コンストラクタテンプレートを呼び出す終わる{0}Shapeを試みるに再帰ことになる上

Array(Shape const&); 

ただし、これは1つのオプションです。オプション#2は:

Array(Array&&); 

暗黙の移動コンストラクタです。それが候補かどうかを確認するために、Array{0}で初期化できるかどうかを確認します。これは基本的にやり直します。この次のレイヤーでは、を0(1つのレイヤーを削除しているため)で初期化できるかどうかを確認します。です。これは受け入れ可能なすべてのコンストラクタテンプレートです。これには2つのユーザー定義変換シーケンスが含まれますが、that's ok for list-initializationが含まれます。

  • オプション#1:{0} --> Shape
  • オプション#2:

    は、だから我々は2つの選択肢があり0 --> Shape --> Array

どちらも他よりも優れているので、呼び出しはあいまいです。


簡単に修正できるのは、コンストラクタテンプレートに実際に範囲になるように制約を追加することです。とにかくis_constructible_v<Shape, int>が真実であることを望まないので、これは一般的には良い練習です...

+0

+1。興味深いことに、 'explicit Array(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うん、それはgccバグだと思う。しかし、 'explicit'は' Shape s(0); 'と似ていないので、実際には十分ではありません。どちらの場合でも制約が必要です。 – Barry

関連する問題