#1664の提案された解決策(提案された解決策1664)を見た後、私は関数テンプレートのデフォルト引数のルールに混乱しています、ここにコンテンツを引用してください:
8.1.5 [expr.prim.lambda]パラグラフ3によると
クロージャタイプは、対応するラムダ式を含む最小のブロックスコープ、クラススコープ、または名前空間スコープで宣言されます。[注:これにより、クロージャタイプに関連付けられた名前空間とクラスのセットが決まります(6.4.2 [basic.lookup.argdep])。ラムダ宣言子のパラメータタイプは、これらの関連する名前空間とクラスには影響しません。—エンドノート]
ただし、17.8.1 [temp.inst]パラグラフ13には、
デフォルトの引数を使用する必要がある方法で関数テンプレートfが呼び出された場合、依存名が検索され、セマンティクスの制約がチェックされ、デフォルトの引数で使用されているテンプレートのインスタンス化は、デフォルトの引数のように行われます。その時点で使用されている関数テンプレートfと同じスコープ、同じテンプレートパラメータ、同じアクセス権を持つ関数テンプレートの特殊化で使用される初期化子でした。
その場合、可能性は、テンプレート関数(または、おそらく、クラステンプレートのメンバー関数)のデフォルト引数のラムダ式のクロージャタイプが、の本体のブロックスコープで宣言されていると見なされることです。架空の機能テンプレートの特殊化。
次の例を考えてみましょう。
namespace J {
inline namespace K {
template <typename T> int zap(const T &t) { foo(t); return 0; }
template <typename T> void zip(int = zap([] { })) { }
}
template <typename T> void foo(const T &) { }
}
void bar() {
J::K::zip<long>();
/*Accroding to the above wording,the invoke just like:
=> J::K::zip<long>(zap([] { }));
*/
}
zipがテンプレートではなかった場合、引数に依存するルックアップは、テストされたすべての実装でfooのルックアップを正常に解決します。ただし、記述されている例の処理には実装の違いがあります。
決議案(2013年9月):
17.8.1 [temp.inst]段落13を次のように変更します。
デフォルトの引数を使用する必要がある方法で関数テンプレートfが呼び出されると、依存名が検索され、セマンティクスの制約がチェックされ、デフォルトの引数で使用される任意のテンプレートが行われているデフォルトの引数は、同じスコープで関数テンプレートの特殊化で使用される初期化子、同じテンプレートパラメータと、その時点で使用され、関数fのテンプレートと同じアクセスされていたかのように除いてクロージャ型が宣言されたスコープ(8.1.5 [expr.prim.lambda])—したがって、関連する名前空間—は、デフォルト引数の定義のコンテキストから決定されたままである。この分析は、デフォルトの引数のインスタンス化と呼ばれます。次に、インスタンス化されたデフォルト引数がfの引数として使用されます。
強調された部分に注意してください、私が誤解しない限り、それは強調された部分がコメント化された場合、名前空間の引数がlike 内のフォームを想定していないため、引数依存のルックアップfoo
によってルックアップできなかったことを[] { }
意味します。したがって、[ expr.prim.lambda]は、第3項の名前空間がにあり、その範囲で、そこにはい強調部分は、名前空間の考慮この場合のためにあるので見つけることができ、内と同じように、それはの名前空間を意味していないですが、これで、親の名前空間で見つけることができますJ
K
function bar
J::K::zip<long>(zap([] { }) /*default argument*/);
[] { }
fuction bar
foo
[] { }
zap
zap
[] { }
K
foo
J
引数依存の検索規則により、これまでのところ、どうかmisundertandこれらのルールは、デフォルトであっても、me.the他のポイントのビューがデフォルトの引数は、関数が呼び出されるたびに評価されていることである修正してください非依存。だから、考慮続けます次のコード:
#include <iostream>
struct A {
};
template<typename T>
int func(T, float) { //#a
std::cout << "float" << std::endl;
return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1
}
template<typename T>
int func(T, int) { //#b
std::cout << "int" << std::endl;
return 0;
}
int main() {
test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
std::getchar();
}
デフォルトの引数func
は依存していませんが、関数test
が呼び出されるたびに決定する必要があり、一部のコンパイラでコードをテストします。
MSVCレポートのすべてのバージョン「INT」、gccの報告書「フロート」、打ち鳴らす報告書「フロート」、GCCまたは打ち鳴らすのレポートにAccroding?地獄何、それはに思えるfunc
時に決定される#1
とMSVCがいることを証明したfunc
時に決定されます#2
。MSVCが間違っている場合、つまり、非依存のデフォルト引数を#1内で決定でき、関数が呼び出されるたびに決定する必要がないため、強調された部分を追加する必要があるのはなぜですか?(強調された部分を正しく理解している場合、ラムダ式が関数宣言の時点でも呼び出しの時点でも、クロージャー型の名前空間をデフォルトの引数内で一貫させることが目的です)。これらのルールを誤解している場合、それらを正しく解釈するにはどうすればよいですか?
更新:
gccのバージョン9.1以降は#1664で言及されているコードをコンパイルできません。エラーを報告します(コンパイル結果)
質問:
1.関数テンプレートまたは非テンプレート関数の非依存デフォルト引数は、対応する関数が呼び出されるたびに決定する必要がありますか?
2.「デフォルト引数の定義」とはどういう意味ですか?これは厳密に言いますか?対応するラムダ式を含むデフォルトの引数を含みます、そうですか?これに関する私の理解が間違っている場合は、私を修正してください)
f
使用される関数テンプレートのスコープと同じスコープ」は実際には、架空の特殊化はJ::K
の定義内ではなく名前空間内にあるように聞こえbar
ますが、変更されたフレーズがどのように変化するかはわかりません。