6

私はコンパイルの問題に苦労しており、問題を小さなコードセグメントに縮小することができました。不完全な型の呼び出し演算子のdecltypeのための特別な振る舞い

ステージを設定するには、基本メソッドが派生クラスの別のものを呼び出すCRTPを実行しようとしています。問題は、末尾の戻り値の型を使用して、直接、派生クラスのメソッドに転送の型を取得することです。が派生クラスの呼び出し演算子に転送されない限り、これは常にをコンパイルできません。

これはコンパイル:

#include <utility> 

struct Incomplete; 

template <typename Blah> 
struct Base 
{ 
    template <typename... Args> 
    auto entry(Args&&... args) 
     -> decltype(std::declval<Blah&>()(std::declval<Args&&>()...)); 
}; 

void example() 
{ 
    Base<Incomplete> derived; 
} 

を、これはしませんが:(注唯一の違いについてのコメント)

#include <utility> 

struct Incomplete; 

template <typename Blah> 
struct Base 
{ 
    template <typename... Args> 
    auto entry(Args&&... args) 
     -> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...)); 
     //    I only added this ^^^^^^^^^^^ 
}; 

void example() 
{ 
    Base<Incomplete> derived; 
} 

私が取得エラー:

<source>: In instantiation of 'struct Base<Incomplete>': 
15 : <source>:15:22: required from here 
10 : <source>:10:58: error: invalid use of incomplete type 'struct Incomplete' 
     -> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...)); 
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ 

があるようですDerivedクラスのdecltypeを解決する際にいくつかの特別な動作が起こります。これを説明するものが標準の中にありますか?

EDIT:メイドさらに大きな簡素化

PS:godbolt上の例をコンパイル:クラステンプレートをインスタンス化https://godbolt.org/g/St2gYC

+0

なぜ 'std :: declval ()'と書かれていませんか?私は、2番目のブロックがodrの '' Incomplete''の使用であると考えていますが、最初のブロックはそうではありません。 –

+0

@PasserByいずれにせよ、それは同じ問題です。私はあなたの提案に変更したので、混乱は少なくなった。 –

+0

あなたはこれを探していますか? https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda –

答えて

4

は、そのメンバ関数テンプレート([temp.inst]/2)の宣言をインスタンス化します。私。宣言(またはセット、

(14.6.2で定義されている)の名前は、テンプレートパラメータに依存していない場合:私たちは今[temp.res]/10を考える宣言

template <typename... Args> 
auto entry(Args&&... args) 
    -> decltype(std::declval<Incomplete&>().operator()(std::declval<Args&&>()...)); 

を見ています宣言) は、その名前がテンプレート定義に現れる場所でスコープ内になければなりません。

実際、operator()の名前はテンプレートパラメータに依存しません。タイプ依存でも値依存でもなく、従属名でもありません。明らかに、範囲に宣言はないので、宣言は不正な形であり、診断は必要ありません。

これに対して、最初のスニペットでは、Incompleteの名前の検索は必要ありません。 x.operator()(...)xクラスタイプであるx(...)の変換は、operator()[over.call] — X内でルックアップされた後にのみ起こる:

したがって、コールx(arg1,...)はx.operator()(ARG1として解釈され、.. 。 のクラスオブジェクトx の場合T​::​operator()(T1, T2, T3)が存在し、 解決メカニズム([over.match.best])によって最適な一致関数として演算子が選択されている場合は です。

これは、2番目のコードが不正な形式になっている段落とは異なります。[temp。res]/10は、の宣言の一部がであること、およびその名前がそれらの宣言にバインドされていることを示しています。上記の変換では、引数型(およびnumber ...)も既知であるため、operator()を呼び出すことを一意に決定できます。つまり、.operator()を挿入するだけではなく、呼び出される演算子関数を常に同時に識別します。演算子のオペランドの型に依存式である場合、オペレータはまた、依存名を表し

:私たちは、[temp.dep]でこの解釈のさらなる確認を見つけることができます。そのような名前はバインドされておらず、テンプレートのインスタンシエーションの時点で参照されます[...]

operator()の引数の型は明らかに型依存です。

+0

驚くばかり!完璧な説明。副題として、私がこの問題を回避する方法は、 'operator()'がテンプレートパラメータに依存するように強制することです。まず、別の宣言 'template T mydeclval();' を導入し、それで 'decltype(mydeclval )演算子(std :: declval ()...)) ' –

関連する問題