web-dev-qa-db-ja.com

#1664の提案された解決策を理解する方法

#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と同じアクセス権を持つ関数テンプレート特殊化で使用されるイニシャライザであるかのように、exceptクロージャタイプが宣言されているスコープ(8.1.5 [expr.prim.lambda])—したがって、それに関連付けられている名前空間—は、デフォルト引数の定義のコンテキストから決定されたままである。この分析は、デフォルトの引数のインスタンス化と呼ばれます。次に、インスタンス化されたデフォルト引数がfの引数として使用されます。

強調された部分に注意してください、私が誤解していない場合、つまり、強調された部分がコメント化されている場合、foo引数に依存するルックアップで検索できませんでした。引数[] { }どの名前空間がJでもKでもない場合は、_J::K::Zip<long>(zap([] { }) /*default argument*/);のようにfunction bar内のフォームを想定し、[expr.prim.lambda]段落3[] { }fuction barにあり、そのスコープではfooが見つからないため、この場合の強調部分は、zap内の[] { }の名前空間をzapと同じと見なし、[] { }の名前空間がKであることを意味しますfooは、親の名前空間にありますJは、引数に依存するルックアップルールで見つけることができます。これまでのところ、これらのルールを誤解している場合は修正してください。もう1つの観点は、関数が呼び出されるたびにデフォルトの引数が評価されることです。デフォルトがnon-dependentの場合:引き続き次のコードを検討してください:

#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レポート「float」、clangレポート「float」、一体何ですか?gccまたはclangのレポートによると、func#1and MSVCで決定されているようですfunc#2で決定されることを証明しました。 MSVCが間違っている場合、つまり、非依存のデフォルト引数を#1内で決定でき、関数が呼び出されるたびに決定する必要がないので、強調された部分を追加する必要があるのはなぜですか?(If私は強調された部分を正しく理解しています、その目的は、ラムダ式が関数宣言の時点であるか、呼び出しの時点であるかに関係なく、デフォルトの引数内でクロージャ型の名前空間を一貫させることです)。これらのルールを誤解している場合、それらを正しく解釈するにはどうすればよいですか?

更新:

gccのバージョン9.1以降は、#1664で言及されているコードをコンパイルできません。エラーを報告します( コンパイル結果

質問:

1.関数テンプレートまたは非テンプレート関数の非依存デフォルト引数は、対応する関数が呼び出されるたびに決定する必要がありますか?

2.「デフォルトの引数の定義」とはどういう意味ですか?この表現は厳密にですか?(言い換えれば、私の理解は、追加されたルールが表現したいactullayの名前空間はcloseure型は、対応するラムダ式を含むデフォルトの引数を含む関数宣言のものです。そうですか?これについての私の理解が間違っている場合は、私を訂正してください

8
jack X

デフォルトの引数は、呼び出されるたびにevaluatedですが、これはランタイムプロパティです。呼び出しは、ソース行ではなく実際の制御フローによってカウントされます。これとは別に、テンプレート化された関数のデフォルトの引数は 定義と見なされます であり、必要な場合はインスタンス化されます、最大で1回関数の特殊化(一致する必要があるインスタンス化の複数のポイントに関する通常の条件付き)。 CWG1664は、そのインスタンス化がwordedであることに基づいて非常に狭い問題でした:fictitious関数テンプレートを導入することにより、それはラムダの宣言が「物理的に」動いた可能性を開いたままにしました。修正は実際にはADLにのみ影響します。

funcの例は、代わりにテンプレートの通常の名前検索ルールを示しています。何回でもwhencetestのデフォルトの引数がインスタンス化されている場合、funcdependentの名前ではないため、func(T,float)(毎回)が見つかります。 [〜#〜] msvc [〜#〜]がこのルールを正しく実装したことがないことは有名です(公正にするために、それらの実装はルールよりも古いため、最近、必要な(そしてほぼ完全な)テンプレートサポートの書き換えを開始しました)。

一方、最近のGCCはCWG1664の例で明らかにバグがあります:fooが使用されているが定義されていないことに不満があることに注意してください矛盾両方はっきり見える{ }とそれが見つからないことに関する以前のエラーメッセージ。

3
Davis Herring