2016-11-16 7 views
1

次のコードでは、比較的シンプルな型消去技術を使用しています。クラスinterpreter_context_ptrは「インタフェース」を定義し、インタフェースを実装するオブジェクトへのポインタを使用してinterpreter_context_ptrを構築することができます。これにより、仮想ディスパッチを使用せずに多形性が可能になります。型消去、デリゲート、およびラムダ関数(msvc)

これは、Impossibly Fast Delegatesという古い記事と非常に似ています。また、githubのbadairでeraserfaceプロジェクトをチェックしてください。

また、初めて認識しない場合、+[](...)の構文は、いわゆる「ポジティブラムダ」構文です。hereは良い説明です。ここで

はMCVEです:テスト中の

#include <iostream> 
#include <string> 
#include <vector> 

class interpreter_context_ptr { 
    void * object_; 
    void (*new_text_call_)(void *, const std::string &); 
    void (*error_text_call_)(void *, const std::string &); 
    void (*clear_input_call_)(void *); 

public: 
    void new_text(const std::string & str) const { 
    this->new_text_call_(object_, str); 
    } 

    void error_text(const std::string & str) const { 
    this->error_text_call_(object_, str); 
    } 

    void clear_input() const { this->clear_input_call_(object_); } 

    template <typename T> 
    explicit interpreter_context_ptr(T * t) 
    : object_(static_cast<void *>(t)) 
    , new_text_call_(+[](void * o, const std::string & str) { 
     static_cast<T *>(o)->new_text(str); 
    }) 
    , error_text_call_(+[](void * o, const std::string & str) { 
     static_cast<T *>(o)->error_text(str); 
    }) 
    , clear_input_call_(+[](void * o) { static_cast<T *>(o)->clear_input(); }) { 
    } 
}; 

/*** 
* Tests 
*/ 

struct A { 
    void new_text(const std::string & str) { 
    std::cout << "A: " << str << std::endl; 
    } 

    void error_text(const std::string & str) { 
    std::cout << "A! " << str << std::endl; 
    } 
    void clear_input() { std::cout << std::endl; } 
}; 

struct B { 
    void new_text(const std::string & str) { 
    std::cout << "B: " << str << std::endl; 
    } 

    void error_text(const std::string & str) { 
    std::cout << "B! " << str << std::endl; 
    } 
    void clear_input() { std::cout << std::endl; } 
}; 

int main() { 
    std::vector<interpreter_context_ptr> stack; 

    A a; 
    B b; 

    stack.emplace_back(&a); 
    stack.back().new_text("1"); 
    stack.emplace_back(&b); 
    stack.back().new_text("2"); 
    stack.emplace_back(&b); 
    stack.back().new_text("3"); 
    stack.back().clear_input(); 
    stack.pop_back(); 
    stack.back().error_text("4"); 
    stack.emplace_back(&a); 
    stack.back().error_text("5"); 
    stack.pop_back(); 
    stack.back().error_text("6"); 
    stack.pop_back(); 
    stack.back().new_text("7"); 

    stack.back().clear_input(); 
    stack.pop_back(); 
    std::cout << "Stack size = " << stack.size() << std::endl; 
} 

コードは、わずか数行非常に簡単で、私は最後の数ヶ月で作業したプロジェクトで、gccと打ち鳴らすに適しています。

しかし私は理解していないMSVCから非常に簡潔なエラーメッセージを受け取ります。

最初に、それは正のラムダについて文句を言っていますが、私はそうすべきではないと思います。

Error(s): 

source_file.cpp(27): error C2061: syntax error: identifier 'T' 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(655): note: see reference to function template instantiation 'interpreter_context_ptr::interpreter_context_ptr<A>(T *)' being compiled 
     with 
     [ 
      T=A 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(918): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled 
     with 
     [ 
      _Alloc=std::allocator<interpreter_context_ptr>, 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(917): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled 
     with 
     [ 
      _Alloc=std::allocator<interpreter_context_ptr>, 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(929): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(928): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(28): error C2593: 'operator +' is ambiguous 
source_file.cpp(28): note: could be 'built-in C++ operator+(void (__cdecl *)(void *,const std::string &))' 
source_file.cpp(28): note: or  'built-in C++ operator+(void (__stdcall *)(void *,const std::string &))' 
source_file.cpp(28): note: or  'built-in C++ operator+(void (__fastcall *)(void *,const std::string &))' 
source_file.cpp(28): note: or  'built-in C++ operator+(void (__vectorcall *)(void *,const std::string &))' 
source_file.cpp(28): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_3268dba1ab087602b708c8fa2c92932b>)' 
source_file.cpp(30): error C2061: syntax error: identifier 'T' 
source_file.cpp(31): error C2593: 'operator +' is ambiguous 
source_file.cpp(31): note: could be 'built-in C++ operator+(void (__cdecl *)(void *,const std::string &))' 
source_file.cpp(31): note: or  'built-in C++ operator+(void (__stdcall *)(void *,const std::string &))' 
source_file.cpp(31): note: or  'built-in C++ operator+(void (__fastcall *)(void *,const std::string &))' 
source_file.cpp(31): note: or  'built-in C++ operator+(void (__vectorcall *)(void *,const std::string &))' 
source_file.cpp(31): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_b251fc93653023678ada88e17e2a71b3>)' 
source_file.cpp(32): error C2061: syntax error: identifier 'T' 
source_file.cpp(32): error C2593: 'operator +' is ambiguous 
source_file.cpp(32): note: could be 'built-in C++ operator+(void (__cdecl *)(void *))' 
source_file.cpp(32): note: or  'built-in C++ operator+(void (__stdcall *)(void *))' 
source_file.cpp(32): note: or  'built-in C++ operator+(void (__fastcall *)(void *))' 
source_file.cpp(32): note: or  'built-in C++ operator+(void (__vectorcall *)(void *))' 
source_file.cpp(32): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_3ba87b1970191b4772ddfa67a05f70ea>)' 
source_file.cpp(28): error C2088: '+': illegal for class 
source_file.cpp(31): error C2088: '+': illegal for class 
source_file.cpp(32): error C2088: '+': illegal for class 

さて、何かがラムダは、暗黙的に関数ポインタに変換され、単項operator +がありますここで、マイクロソフトの土地で「正ラムダ」理論、と間違っているのです(私はrextester.comから、これらのエラーを貼り付けています)ノーオペレーション。それはいいです、正のラムダを取り除きましょう。

template <typename T> 
    explicit interpreter_context_ptr(T * t) 
    : object_(static_cast<void *>(t)) 
    , new_text_call_([](void * o, const std::string & str) { 
     static_cast<T *>(o)->new_text(str); 
    }) 
    , error_text_call_([](void * o, const std::string & str) { 
     static_cast<T *>(o)->error_text(str); 
    }) 
    , clear_input_call_([](void * o) { static_cast<T *>(o)->clear_input(); }) { 
    } 

MSVCはまだ幸せではない判明 - 主なエラーが不可解C2061です。

コンパイラは、予想されなかった識別子を見つけました。それを使用する前に識別子が宣言されていることを確認してください。

初期化子は、かっこで囲むことができます。この問題を回避するには、宣言子をかっこで囲むか、typedefにします。

このエラーは、コンパイラが式をクラステンプレートの引数として検出した場合にも発生する可能性があります。 typenameを使用してコンパイラに型を伝えます。ここで

は完全なログです:

Error(s): 

source_file.cpp(27): error C2061: syntax error: identifier 'T' 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(655): note: see reference to function template instantiation 'interpreter_context_ptr::interpreter_context_ptr<A>(T *)' being compiled 
     with 
     [ 
      T=A 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(918): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled 
     with 
     [ 
      _Alloc=std::allocator<interpreter_context_ptr>, 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(917): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled 
     with 
     [ 
      _Alloc=std::allocator<interpreter_context_ptr>, 
      _Ty=interpreter_context_ptr, 
      _Objty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(929): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(928): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled 
     with 
     [ 
      _Ty=interpreter_context_ptr 
     ] 
source_file.cpp(30): error C2061: syntax error: identifier 'T' 
source_file.cpp(32): error C2061: syntax error: identifier 'T' 

私はT年代の前にどこにでもtypenameを入れての彼らの提案をしようと、それは何も解決しない、と私はより多くの有益なエラーメッセージを得ることはありません。


ここには何がありますか?ここでC2061の "識別子"エラーが意味することは、MSVCがラムダ本体の内部のテンプレートパラメータを追跡できないこと、またはそれを間違って解析していることを意味し、Tは変数であり型ではないと思いますか?

このMSVC 2015のようなテンプレート関数内にあるラムダ内のテンプレートパラメータを参照することはできませんか?

ラムダを除外して静的なテンプレート関数を作成するだけでいいですか?


それは私がちょうど完全にラムダを取り除くなどのようなテンプレート関数を使用して取得した場合、その後、MSVCは罰金それをコンパイルすることが判明:

class interpreter_context_ptr { 
    void * object_; 
    void (*new_text_call_)(void *, const std::string &); 
    void (*error_text_call_)(void *, const std::string &); 
    void (*clear_input_call_)(void *); 

    template <typename T> 
    struct helper { 
    static void new_text(void * o, const std::string & str) { 
     static_cast<T*>(o)->new_text(str); 
    } 
    static void error_text(void * o, const std::string & str) { 
     static_cast<T*>(o)->error_text(str); 
    } 
    static void clear_input(void * o) { 
     static_cast<T*>(o)->clear_input(); 
    } 
    }; 

public: 
    void new_text(const std::string & str) const { 
    this->new_text_call_(object_, str); 
    } 

    void error_text(const std::string & str) const { 
    this->error_text_call_(object_, str); 
    } 

    void clear_input() const { this->clear_input_call_(object_); } 

    template <typename T> 
    explicit interpreter_context_ptr(T * t) 
    : object_(static_cast<void *>(t)) 
    , new_text_call_(&helper<T>::new_text) 
    , error_text_call_(&helper<T>::error_text) 
    , clear_input_call_(&helper<T>::clear_input) { 
    } 
}; 

をしかし、私はコメントで書いたように、私は」私は実際にこれを遠くに行かなければならないとかなり驚きました。 MSVCの開発者は、this C++11 feature pageにラムダ関数を完全にサポートし実装していると主張しています。私はラムダの範囲で周囲のテンプレートパラメータを参照できることを含むべきだと思います。

+0

あなたはClan/C2コンパイラフロントエンドに付属しているVS2015を使用しています。 MSVCによってスローされたエラーメッセージを理解できない場合は、別のコンパイラを試してみてください。 MSVCはそうではないが、Clangはこのコードを受け入れるかもしれないことに注意してください。それから、あなたは運がない。しかし、そうでない場合には、どんな場合でも明確なエラーメッセージが表示されます。 – rubenvb

+0

Harm、残念ながら、少なくともClangとGCCは警告なしでこのコードを受け入れているようですので、Clang/C2はMicrosoft C++ライブラリを使用していますが、エラーの原因になる可能性があります。この場合、ライブラリのバグです。 – rubenvb

+0

また、ラムダから '+'を削除してみてください。私はそのコンストラクトのMSVCチョークの前に読んでいます。コードはコンパイルされずにコンパイルされますが、動作は同じであることを確認してください。 – rubenvb

答えて

1

この質問の時点で、Visual C++ 2015コンパイラはラムダのoperator+に問題があります。あなたのラムダの前にある+サインを削除すると、これがコンパイルできます。それはここでは重要ではないと思うが、セマンティクスを少し変更する(表現の有効なタイプはわずかに異なる)。

最新のアップデートがインストールされていることを確認し、他のコンパイラ/ライブラリのバグが原因で時間を失わないようにしてください。

1

MSVCはラムダを複数の関数ポインタ呼び出し型に変換する点で+に問題があります。だから+はあいまいです。

通常、暗黙のキャスト演算子が正しいことを行い、ラムダをキャストしているポインタの呼び出し規約について知っているので、+をドロップするとコードが「うまく動作します」。

これはあなたの唯一の問題ではありません。次の問題は、ステートレスラムダが、周囲のコンテキストで利用可能な型に正しくアクセスできないことです。

MSVC 2015は、名目上はC++ 11コンパイラです。それは正しく動作しない多くの、多くの場合があります。コンパイラの更新により、これらの種類のサポートが大幅に改善される傾向があります。 MSVC 2015 U3.1(U3があり、U3上にU3.1と呼ばれるパッチがあります)があることを確認してください。

Tconstの場合をもう少し詳しく説明します。私は怠惰になり、を(void*)tに置き換えます。これは、constを取り除き、1つのステップでvoidにキャストするためです。あるいは、const_cast<void*>(static_cast<const volatile void*>(t))でもそうであってもかまいません。

関連する問題