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


8

#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項の名前空間がにあり、その範囲で、そこにはい強調部分は、名前空間の考慮この場合のためにあるので見つけることができ、内と同じように、それはの名前空間を意味していないですが、これで、親の名前空間で見つけることができますJKfunction barJ::K::zip<long>(zap([] { }) /*default argument*/);[] { }fuction barfoo[] { }zapzap[] { }KfooJ引数依存の検索規則により、これまでのところ、どうか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.「デフォルト引数の定義」とはどういう意味ですか?これは厳密に言いますか?対応するラムダ式を含むデフォルトの引数を含みます、そうですか?これに関する私の理解が間違っている場合は、私を修正してください


1
f使用される関数テンプレートのスコープと同じスコープ」は実際には、架空の特殊化はJ::Kの定義内ではなく名前空間内にあるように聞こえbarますが、変更されたフレーズがどのように変化するかはわかりません。
aschepler

私はそれをすべて理解していますが、変更/太字部分の前の文言から同じことを結論付けたとは思いません。
aschepler

1
「今、私は追加の質問に対する報奨金を始めます。」これはStackOverflowの動作方法ではありません。新しい質問がある場合は、新しい質問をしてください。質問ごとに1つの質問。編集をロールバックしました。
バリー

@バリー更新された質問は「提案された#1664の解決策を理解する方法」にあると思います
ジャックX

新しい質問を投稿する必要があります。Stack Overflowは、将来の訪問者のために役立つ質問とその回答のリポジトリを構築するためのものです。最初の質問に回答があるため、このページを1つの全体として表示します。回答者がソリューションの提供に費やした作業を尊重し、新しい質問を新しい質問投稿に入力してください。ここで問題を変更(拡大)すると、そうでなければ回答が無効になり、投稿がトピックから外れます(「より多くの焦点が必要」として閉じられます)。
Martijn Pieters

回答:


3

デフォルトの引数は呼び出されるたびに評価されますが、これはランタイムプロパティです。呼び出しはソース行ではなく実際の制御フローによってカウントされます。それとは別に、テンプレート化された関数のデフォルト引数は定義見なされ、必要に応じてインスタンス化されます(関数の特殊化ごとに1回まで(インスタンス化の複数の点についての通常の条件は一致しなければなりません))。CWG1664は、そのインスタンス化がどのように表現されたかに基づく非常に狭い問題でした。架空の関数テンプレートを導入することにより、ラムダの宣言が「物理的に」動いた可能性を残しました。修正は実際にはADLにのみ影響します。

あなたのfunc例ではなく、テンプレート内の通常の名前ルックアップ規則を示していますどのように多くの時間と関係なく、そこから testデフォルトの引数は、インスタンス化されるのfuncそれではない依存名ので、発見func(T,float)(毎回)。 MSVCがこのルールを正しく実装したことはないことは有名です(公平を期すために、それらの実装はルールよりも古いため、テンプレートサポートの必要な(そしてほぼ完全な)書き換えを最近開始したためです)。

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


私はあなたの答えについて私の理解を要約しようとします。デフォルトの引数の場合、それらが非依存名である場合、名前はその時点で書かれているとおりにバインドされます([dcl.fct.default] / 5が言ったように)。代わりに、依存名は、関数が呼び出されるたびに名前を検索する必要があります呼ばれた。2.ラムダ式の場合、名前ではないため、デフォルトの引数が非依存であってもバインドできません。したがって、強調する部分が制約されていない場合、クロージャタイプは、ポイントに応じて異なる名前空間を持つことになります。名前空間がそれをカバーする呼び出しの。同意しますか?
ジャックX

私が個別に話す必要があるあなたの回答からの別の質問は、「架空の関数テンプレートの特殊化」は「J :: K :: zip」の定義ではなく、「J :: K :: zip <long>();」を参照しますかインスタンス化の時点で<long>() "?
ジャックX

@jackX:それと、非依存名の場合は[temp.nondep] / 1です。従属名は、デフォルトの引数に現れるかどうかにかかわらず、すべてのインスタンス化について検索されます。クロージャタイプは架空の関数テンプレートの特殊化に属します(これは呼び出しでも実際の定義でもありませんzip)。
Davis Herring

したがって、「架空の関数テンプレートの特殊化」は「J :: K :: zip <long>();」のようなものを指しますか?そして、「クロージャ型が宣言されているスコープを除いて」の効果は何ですか、私は文がそれに続いたと思います:「したがって、それに関連する名前空間—デフォルト引数の定義のコンテキストから決定されたままで十分です」 ADLへ
ジャックX

@jackX:私はそれがそう言及しなかったとちょうど言った。スコープは実際のルールです。名前空間はすべてのインポートを運ぶとしても当然です。
Davis Herring
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.