42

C ++標準§13.3.1.7[over.match.list]では、次のように記述されています。

copy-list-initializationでは、explicitコンストラクターが選択されている場合、初期化の形式が正しくありません。

これが、たとえば次のようなことができない理由です。

struct foo {
    // explicit because it can be called with one argument
    explicit foo(std::string s, int x = 0);
private:
    // ...
};

void f(foo x);

f({ "answer", 42 });

(ここで行われるのは変換ではなく、コンストラクターが「暗黙的」であったとしても変換されないことに注意してください。これはfoo、コンストラクターを直接使用したオブジェクトの初期化です。以外はstd::string、ここでは変換はありません。)

これは私には完全に問題ないようです。暗黙の変換が私を噛む方法はありません。

{ "answer", 42 }他の何かを初期化できる場合、コンパイラは私を裏切って間違ったことをしません:

struct bar {
    // explicit because it can be called with one argument
    explicit bar(std::string s, int x = 0);
private:
    // ...
};

void f(foo x);
void f(bar x);

f({ "answer", 42 }); // error: ambiguous call

問題はありません。呼び出しがあいまいで、コードがコンパイルされないため、オーバーロードを明示的に選択する必要があります。

f(bar { "answer", 42 }); // ok

禁止事項が明記されているので、ここで何かが足りない気がします。私が見る限り、明示的なコンストラクターを選択するリスト初期化は私には問題のようには思えません。リスト初期化構文を使用することにより、プログラマーはすでにある種の「変換」を実行したいという願望を表明しています。

何がうまくいかない可能性がありますか?私は何が欠けていますか?

4

4 に答える 4

28

概念的には、copy-list-initializationは、複合値を宛先タイプに変換することです。言い回しを提案し、理論的根拠を説明した論文は、「コピーリストの初期化」の「コピー」という用語を、その背後にある実際の理論的根拠を実際に伝えていないため、残念ながらすでに考慮しています。ただし、既存の文言との互換性のために保持されています。文字列はペアではないため、{10, 20}ペア/タプル値はコピー初期化できません。String(int size, int reserve)

明示的なコンストラクターが考慮されますが、使用することは禁止されています。これは、次のような場合に意味があります

struct String {
  explicit String(int size);
  String(char const *value);
};

String s = { 0 };

0文字列の値を伝達しません。したがって、両方のコンストラクターが考慮されるため、これはエラーになりますが、ヌルポインター定数として扱われるexplicit代わりに、コンストラクターが選択されます。0

残念ながら、これは関数間の過負荷解決でも発生します

void print(String s);
void print(std::vector<int> numbers);

int main() { print({10}); }

これもあいまいさのために形式が正しくありません。C ++ 11がリリースされる前の一部の人々(私を含む)は、これは残念なことだと思っていましたが、これに関する変更を提案する論文を思いつきませんでした(私が知る限り)。

于 2012-02-06T10:26:06.933 に答える
2

暗黙のキャストを停止するために「明示的」があり、暗黙のキャストを実行するように要求しているからではありませんか?

単一の引数コンストラクターで構造を指定したかどうかを質問しますか?

于 2012-02-06T08:46:02.780 に答える
2

この文:

copy-list-initializationでは、explicitコンストラクターが選択されている場合、初期化の形式が正しくありません。

多くのことを意味します。その中で、明示的なコンストラクターを調べる必要があることを意味します。結局のところ、それを見ることができない場合、明示的なコンストラクターを選択することはできません。ブレースリストを変換する候補を探すときは、すべての候補から選択する必要があります。後で違法であることが判明するものでさえ。

過負荷の解決によって複数の機能が同等に実行可能になる場合は、ユーザーの手動介入を必要とするあいまいな呼び出しが発生します。

于 2012-02-06T09:39:26.300 に答える
1

私が理解しているように、キーワードexplicitの目的は、このコンストラクターで暗黙のキャストを拒否することです。

では、なぜ明示的なコンストラクターを暗黙的なキャストに使用できないのかと質問しているのでしょうか。明らかに、そのコンストラクターの作成者は、明示的なキーワードを使用して明示的に拒否したためです。あなたが投稿した標準からの引用は、明示的なキーワードが初期化子リストにも適用されることを示しています(あるタイプの単純な値だけではありません)。

追加:

もっと正確に言うと、あるコンストラクターで明示的に使用されるキーワードの目的は、このコンストラクターがどこかで使用されていることを完全に明確にすることです(つまり、すべてのコードにこのコンストラクターを明示的に呼び出すように強制します)。

f({a,b})また、 when isのようなIMOステートメントfは、関数の名前であり、明示的なコンストラクター呼び出しとは関係ありません。ここでどのコンストラクター(およびどのタイプ)が使用されているかは完全に不明確です(コンテキストに依存します)。たとえば、存在する関数のオーバーロードに依存します。

一方、のようなものはまったく異なるものです。2つの引数を取る型のコンストラクターを使用し、型の単一の引数を受け入れるのに最適な関数のオーバーロードを使用f(SomeType(a,b))することは絶対に明らかです。SomeTypea,bfSomeType

したがって、一部のコンストラクターは暗黙の使用に問題がなく、他のコンストラクターはf({a,b})、それらの使用の事実が読者に完全に明確である必要があるため、明示的に宣言します。

ADD2:

私のポイントは、何も問題がない場合でも、コンストラクターを明示的に宣言することが絶対に理にかなっている場合があるということです。コンストラクターが明示的であるかどうかのIMOは、あらゆる種類の警告よりもロジックの問題です。

例えば

double x = 2; // looks absolutely natural
std::complex<double> x1 = 3;  // also looks absolutely natural
std::complex<double> x2 = { 5, 1 };  // also looks absolutely natural

だが

std::vector< std::set<std::string> >  seq1 = 7; // looks like nonsense
std::string str = some_allocator; // also looks stupid
于 2012-02-06T08:45:18.147 に答える