クラステンプレートのコンストラクタ引数が自動的に決定されないのはなぜですか?

次のクラスを考えてみましょう:

template
class Pair
{
     public:
         T1 First;
         T2 Second;
         Pair(const T1 &First, const T2 &Second) : First(First), Second(Second) { }
}

C ++では次のことはできません:

auto p = Pair(10, 10);

それはなぜ許されないのですか?型はコンストラクタ呼び出しから完全に判別できます。
私はそのような回避策があることを知っている:

template
Pair MakePair(const T1 &First, const T2 &Second)
{
    return Pair(First, Second);
}

しかし、なぜそれが必要ですか?コンパイラは関数テンプレートのように引数から型を決定するのはなぜですか?あなたは標準がそれを許さないので、それを言うかもしれません、なぜ標準はそれを許さないのですか?

編集:
これを言う人のために、これは許可されてはならない理由の例です:

template
class Pair
{
     public:
         T1 First;
         T2 Second;
         Pair(const T1 &First, const T2 &Second) : First(First), Second(Second) { }
         Pair(const T2 &Second, const T1 &First) : First(First), Second(Second) { }
};

auto p = Pair(10,1.0);

関数テンプレートのオーバーロードでこれを正確に行うことができます:

template
Pair MakePair(const T1 &First, const T2 &Second)
{
    return Pair(First, Second);
}
template
Pair MakePair(const T2 &Second, const T1 &First)
{
    return Pair(First, Second);
}

なぜこれは機能には許されていますが、クラスには許されませんか?

13
@GeneBushuyev:あなたは、クラスを "呼び出す"ことによってコンストラクタが必要であると指定します。
追加された 著者 Dani,
最後の2つの MakePair の実装はあいまいですが、それらを記述することはできますが、関数テンプレートの型を指定する必要があります。
追加された 著者 David Rodríguez - dribeas,
関数テンプレートのオーバーロードでこれを正確に行うことができます:同じ理由で、実際には、できません。あいまいです。 codepad.org/axjCAZyL
追加された 著者 Mooing Duck,
@Mooing:私は彼がコンストラクタのオーバーロードで存在するような関数オーバーロードで同じあいまいさを作成できると言っていると思います。この場合、テンプレート引数を指定する必要があります。
追加された 著者 Benjamin Lindley,
コンストラクタには名前がなく、呼び出すことも不可能なため、引数の減算は関数呼び出しに対してのみ機能します。
追加された 著者 Gene Bushuyev,

3 答え

これはBjarne Stroustrupがこの問題について言わなければならないものです:

クラステンプレートの引数は決して推論されません。その理由は   あるクラスのためのいくつかのコンストラクタによって提供される柔軟性   そのような控除は多くの場合不可能になり、多くの場合は不明瞭になる   もっと。

10
追加された
関数テンプレートのオーバーロードについても同じことが言えます
追加された 著者 Dani,
編集の例を追加しました
追加された 著者 Dani,
違いは、型が作成されていないため、関数テンプレートのオーバーロードによって作成される型についてあいまいさがないことです。上記はよく言われていませんが、あなたが私の意味を理解してくれることを願っていますか?
追加された 著者 John Dibling,
長い間、 auto が初めて導入されたとき、私はその構文を引数の減算に使用することを提案しました。 pair (10,10)。これは、 mypair p(10,10); の代わりに mypair p(10,10); を書くことができるように、自動p = make_pair(10,10); その構文は、(型減算以外の目的を果たさない)ヘルパー関数 make_something()を不要にするでしょう。
追加された 著者 Gene Bushuyev,

以下を考慮する。

template
class Pair
{
     public:
         T1 First;
         T2 Second;
         Pair(const T1 &First, const T2 &Second) : First(First), Second(Second) { }
         Pair(const T2 &Second, const T1 &First) : First(First), Second(Second) { }
};

auto p = Pair(10,1.0);

Should p be a Pair or a Pair?

5
追加された
@デビッド:それはあいまいではない場合は、なぜそれが許可されていないのですか?あなたはそれがあいまいであるかもしれないと言ったので、あまりにもあいまいかもしれない関数の例を提供しましたが、一般的なケースでは関数では許可されていますが、一般的なケースではクラスでは許可されません。
追加された 著者 Dani,
編集の例を追加しました
追加された 著者 Dani,
関数テンプレートのオーバーロードでも同じことができます。
追加された 著者 Dani,
@ベンジャミン:それは曖昧ではないので。
追加された 著者 John Dibling,
@Daniは、コンパイラやユーザープログラムの複雑さを補うものではないため、一般的な機能は削除されます。 std :: min(10、5.0)の型は何か(コンパイラを使用せずに)知っていますか?
追加された 著者 David Rodríguez - dribeas,
@Dani:追加された例(2つの MakePair )はあいまいです。あなたはそれを書くことができますが、手作業で型を指定することなくそれらを呼び出すことはできません。つまり、型を明示的にする必要があります。
追加された 著者 David Rodríguez - dribeas,
@BenjaminLindley:これまでのこのページのすべてのコードで、 Pair ()は2つの異なる機能と一致します。 タイプはあいまいではなく、関数です。
追加された 著者 Mooing Duck,
エル、おっと。 Pair :: Pair(const T1&First、const T2&Second) Pair :: Pair(const T2&Second)の両方に一致するペア(10,10.0) &First) MakePair(10,10.0) MakePair(const T1&First、const T2&Second) MakePair(const T2&Second、const T1&First)したがって、すべての場合において、呼び出しはあいまいです。
追加された 著者 Mooing Duck,
ああ、OPの元のクラスでは、あいまいさはありません。私は、言語設計者がそれを禁じている理由を考えることはできません。 Bjarne Stroustrupの言葉は本当に大きな理由ではありません。
追加された 著者 Mooing Duck,
@ダニ:私はあなたのポイントを参照してください。なぜそれが明確でない場合には型を推論できないのでしょうか?私はそれについて考える必要があります。
追加された 著者 Benjamin Lindley,
@ジョン:私が示したクラスのような過負荷がなく、専門化されていない場合、どのように曖昧ですか?
追加された 著者 Benjamin Lindley,
IOW、 Pair (10,10.0)の他にペア(10,10.0)の意味は?
追加された 著者 Benjamin Lindley,
@モーニング・ダック:私はあなたに従っていない。これまでのこのページのすべてのコードでは、 Pair ()は、クラスやDaniのどちらもデフォルトコンストラクタを持たないため、何も一致しません。
追加された 著者 Benjamin Lindley,
@Mooing:しかし、OPの元のクラスについて言及していますが、ここではあいまいさはどこにありますか?ペア::ペア(const T2&Second、const T1&First)はありません。
追加された 著者 Benjamin Lindley,
@ダニ:どのように、正確に?
追加された 著者 Benjamin Lindley,

一般的にもっとよくはありません。システムを設計する必要があるときは、さまざまな機能がどのように相互作用するかに注意しなければならず、ユーザーに最も効果的な最もシンプルなソリューションを選択する必要があります。典型的な質問は、ユーザーに何を提供するのかフィーチャーにはどのようなコストが必要かフィーチャーにはどのような問題があるか

この場合、型減算を提供すると、変数を宣言するときに型の指定をスキップしたり、コンストラクタを直接呼び出すときに役立ちます。

pair p = pair( 10, 20 );

変数の宣言は auto で行うことができるので、もはや問題にはなりませんが、この機能の動機づけになっている可能性があります。コンストラクタの呼び出しの場合、追加された値は非常に限られています。標準ライブラリにあるように、名前付きコンストラクタを提供することができます:

auto p = make_pair( 10, 20 );

ですから、いつもオブジェクトを作成する名前付き関数を提供できるので、コンストラクタを直接呼び出すことができるという付加価値は実際には限られています。潜在的な余分なコピーは最適化されているので、 make_pair を呼び出すとコンストラクタ `pair(10,20)

さて、この機能がユーザーコードに与える可能性のある否定的な影響に注目してください。この機能は、競合するコンストラクタのオーバーロードがなく、関数テンプレートの最適なオーバーロードを検出するのと同じ方法で使用できますが、もっと重要なのは特殊化です。コンパイラは、クラステンプレートのすべての潜在的な特殊化をルックアップし、それぞれが提供するコンストラクタのセットを決定し、最適なコンストラクタのオーバーロードとそれを囲むクラステンプレートを解決する必要があります。

指定した特定の例を見ると、 std :: pair は特殊化されていないので、簡単な例ですが、上記の例のコードは失敗します。

pair p = pair( 10, 20 );

どうして?これは実際問題ですが、この機能はユーザーにかなり混乱を招きます。上で触れたように、式の右辺は、(特殊化がないと仮定して)プレーンなオーバーロードの解法で簡単に解決できます。また、それらの引数は両方とも int 。したがって、最初のステップでは式を次のように推論します。

pair p = pair(10,20);

Now, that is equivalent to pair p( pair( 10, 20 ) ), and we need the second step of resolution, and at this point, the compiler will see that there is a templated constructor:

template 
struct pair {
   first_type first;
   second_type second;

   template 
   pair( U f, V s ) : first(f), second(s) {}
};

That means that every potential instantiation of std::pair has a perfect match for that constructor call, and the compiler cannot select, nor provide any sensible error message other than ambiguous in too many ways.

Now imagine coming to your favorite Q&A forum on the internet and guess how many questions would arise from this type of details. Ignoring the cost of implementing the feature, the result of having it in the language is that the language would be more complex and harder to write, it would impose an even steeper learning curve, and all that for basically no real value: in the cases where deduction can be applied, it is trivial to write a helper function that will provide the same advantages, like make_pair with no added cost.

あなたが型を提供しなければならないという事実は、ユーザから複雑さを取り除きます。コンパイラが私に正しい型を推論するかどうかを考える必要はありません。これらの詳細に焦点を当てなくても、あなたが解決したい実際の問題を考える時間が増えます。

less より良い場合もあります。

ところで、 auto はこれを最も簡単な方法で解決します。そのタイプを一致させて推測するコンストラクタを見つける必要はありませんが、右辺の式ははるかに簡単です。単一のオブジェクトでのみ動作し、そのオブジェクトを新しいオブジェクトに使用します。さらに、単純化されたユースケースでは、ソリューションの合意が得られるまで、 auto の控除を使用して参照を作成できるかどうか、または > const または volatile は推論型の一部です

ちょっと考えました。コンパイラを使用せずに、 std :: min(10、5.0)という式のタイプは何ですか?

2
追加された
一般的な複雑さの点で議論するのではなく、特定の問題を「テンプレート化されたコンストラクタがあることを確認してください... pair(U f、V s)」 - ( cppreference でstd :: pair(pair &&)(8)を使用しますか?
追加された 著者 Tony Delroy,
ヘルパー関数の作成、同期の維持、名前空間の汚染などの負担を過小評価します。テンプレートパーザライブラリを書く私は多くの関数がヘルパー関数であることを発見しました。実際には、ヘルパーのみの _function.hファイルがたくさんあります。 make_something の使用は通常悪い命名であり、事実上すべてのクラスをヘルパー経由でインスタンス化しなければならないため、関数は関連する名前を取り、衝突を避ける型は接頭辞で変更することができます。
追加された 著者 Gene Bushuyev,