関数の引数としての{}が曖昧にならないのはなぜですか?


20

このコードを考えてみましょう:

#include <vector>
#include <iostream>

enum class A
{
  X, Y
};

struct Test
{
  Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
  { std::cout << "vector overload" << std::endl; }

  Test(const std::vector<double>&, int, A = A::X)
  { std::cout << "int overload" << std::endl; }
};

int main()
{
  std::vector<double> v;
  Test t1(v);
  Test t2(v, {}, A::X);
}

https://godbolt.org/z/Gc_w8i

これは印刷します:

vector overload
int overload

あいまいなオーバーロードの解決が原因でコンパイルエラーが発生しないのはなぜですか?2番目のコンストラクターが削除されると、vector overload2回取得されます。どのように/メトリック何によってであるintため、明確に、より良い試合{}よりはstd::vector<int>

コンストラクタのシグネチャは確実にさらにトリミングできますが、同等のコードにだまされて、この質問で重要なものが失われていないことを確認したいと思います。


{}コードのブロックとして正しく呼び出すと、変数に0が割り当てられます-例:const char x = {}; は0(null char)に設定され、intなどと同じ
Seti

2
@Setiこれは{}特定の特殊なケースで効果的に機能しますが、一般的には正しくありません(初心者の場合、std::vector<int> x = {};機能しますが、std::vector <int> x = 0;そうではありません)。「{}ゼロを割り当てる」ほど単純ではありません。
Max Langhof

そう、それほど単純ではありませんが、それでもゼロを割り当てます-この動作は非常に混乱し、実際には使用すべきではないと思います
Seti

2
@Setiは、struct A { int x = 5; }; A a = {};それが構築し、あらゆる意味でゼロを割り当てていないAa.x = 5。これはA a = { 0 };a.x0に初期化するとは異なります。ゼロはに固有ではなく{}、各タイプがデフォルトで構築または値で初期化される方法に固有です。参照してください。ここでここここ
Max Langhof

私はまだデフォルトで作成された値が混乱していると思います(常に動作を確認するか、多くの知識を維持する必要があります)
Seti

回答:


12

それは[over.ics.list]にあり、私のものを強調しています

6それ以外の場合、パラメーターが非集約クラスXであり、[over.match.list]ごとのオーバーロード解決は、引数初期化子リストからX型のオブジェクトの初期化を実行するために、Xの単一の最良コンストラクターCを選択します。

  • Cが初期化子リストコンストラクターでなく、初期化子リストに cv U型の単一の要素がある場合(UはXまたはXから派生したクラス)、暗黙の変換シーケンスは、UがXの場合は完全一致ランク、UがXの場合は変換ランクUはXから派生します。

  • それ以外の場合、暗黙の変換シーケンスはユーザー定義の変換シーケンスで、2番目の標準変換シーケンスはID変換です。

9それ以外の場合、パラメーターの型がクラスでない場合:

  • [...]

  • 初期化子リストに要素がない場合、暗黙の変換シーケンスは恒等変換です。[例:

    void f(int);
    f( { } ); // OK: identity conversion

    終了例]

std::vectorコンストラクタと大胆と見なして、ユーザー定義のconverisonで銃弾によって初期化されます。一方、の場合int、これは恒等変換であるため、最初の引用者のランクを上回ります。


はい、正しいようです。
コロンボ

この状況が標準で明示的に考慮されていることを確認すると興味深いです。私はそれが曖昧であることを本当に期待していました(そしてそれは簡単にそのように指定できたように見える)。私はあなたの最後の文で推論を追跡することはできません- 0タイプがありますintが、入力しないでstd::vector<int>の「型なし」自然にWRT「の場合のように」ということですか、{}
Max Langhof

@MaxLanghof-別の見方をすると、非クラスタイプの場合、ストレッチによるユーザー定義の変換ではありません。代わりに、それはデフォルト値の直接初期化子です。したがって、この場合はIDです。
StoryTeller-Unslander Monica

その部分は明らかです。ユーザー定義のへの変換が必要なことに驚いていますstd::vector<int>。あなたが言ったように、私は「パラメータのタイプが引数のタイプが何であるかを決定することになる」と予想しました、そして{}「タイプ」の(いわば)std::vector<int>はを初期化するために(非アイデンティティ)変換を必要とすべきではありませんstd::vector<int>。規格は明らかにそうしていると言っているので、それはそれですが、私には意味がありません。(注意してください、私はあなたや標準が間違っていると主張しているのではなく、単に私のメンタルモデルでこれを調整しようとしています。)
Max Langhof

わかりました、その編集は私が望んだ解像度ではありませんでしたが、十分に公正でした。:Dお時間をいただきありがとうございます!
Max Langhof
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.