2016-11-03 14 views
8

関数の引数型を調べるメソッドが必要です。そのため、以下のようにclosure_traitsクラスを作成しました。具体的には、Is it possible to figure out the parameter type and return type of a lambda?からインスピレーションを受けています。C++ Lambdaにoperator()がありません

しかし、単純なラムダに適用しようとすると、 'operator()'が '(ラムダ型)'のメンバーではないというエラーが表示されます。 しかし、cppreferenceによると、ラムダにはoperator()があります。 私もstd :: functionを使ってみましたが、同等のエラーがありました。 私は何がうまくいかないのか分かりませんし、どんな助けも大歓迎です。

#include<type_traits> 
#include<tuple>                   
#include<utility> 
#include<iostream>                          

/* For generic types use the type signature of their operator() */              
template <typename T>                      
struct closure_traits : public 
         closure_traits<decltype(&T::operator())> {};           

/* Otherwise, we do a template match on a function type. */ 
template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>             
{ 
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;            
    using Ret = ReturnType;         

    /* The argument types will be the same as the types of the 
    * elements of a tuple composed of them. 
    */                        
    template <std::size_t I>  
    struct Args {   
     using type = typename std::tuple_element<I, 
             std::tuple<ArgTypes...>>::type;                              
    };                                                           

};                                                             

int main() {                              
    auto thing = [=] (int x) {return x;}; 

    std::cerr << "The number of arguments is " 
       << closure_traits<decltype(thing)>::arity << std::endl;                              

    return 0;                              
}  

コンパイラのエラーメッセージは次のとおりです。 コンパイルコマンドはg ++ -std = C++ 14 main.cppです。

main.cpp: In instantiation of ‘struct closure_traits<int (main()::<lambda(int)>::*)(int) const>’: 
main.cpp:9:8: required from ‘struct closure_traits<main()::<lambda(int)> >’ 
main.cpp:34:82: required from here 
main.cpp:9:56: error: ‘operator()’ is not a member of ‘int (main()::<lambda(int)>::*)(int) const’ 
struct closure_traits : public closure_traits<decltype(&T::operator())> {};           
                ^
main.cpp: In function ‘int main()’: 
main.cpp:34:51: error: ‘arity’ is not a member of ‘closure_traits<main()::<lambda(int)> >’ 
std::cerr << "The number of arguments is " << closure_traits<decltype(thing)>::arity << std::endl; 
+0

なぜこれがC++ 14の悪い考えであるのか、テンプレートの 'auto'引数のために知っていますよね? 「これらの型の引数の数」を問いかけることは、しばしばより良い考えです。 – Yakk

+0

私はあなたの質問を理解するか分からない。私は関数の引数の数とその型を推測しようとしています。なぜそれが自動車の影響を受けるのだろうか? – sangrey

+1

あなたのデザインは基本的に '[](auto x){std :: cout << x <<" \ n ";}'をサポートすることはできません。 C++の呼び出し可能変数は固定数の引数を持ちませんし、引数の型も固定されていません。 C++で 'int'の基底を決定しようとするようなものです:' int'は基底を持っていないので、概念は実際には適用されません。あなたのケースでは、*いくつかの* callablesのために、問題を混乱させるarityとargumentの型を固定することができます。 – Yakk

答えて

8

特殊化がdecltype(&T::operator())引数と一致しません。

このため、(あなたが望むように)特殊化を選択する代わりに、コンパイラは再帰的に同じメインテンプレートを選択します。これにより、既に適用された後に再び&T::operator()式が適用されます。すなわち、最初に&T::operator()を実行しようとする試みは実際には成功するが、が既にint (main()::<lambda(int)>::*)(int) constであるときに、コンパイラは&T::operator()を再び適用しようとする。後者にはoperator()が含まれていないため、このエラーメッセージが表示されます。

特殊化を選択できない理由は、テンプレートパラメータ宣言にconstがありません。ラムダのoperator()は、実際にはラムダクラスのメンバーであるconstです。あなたの専門分野

template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const> 
... 

の宣言にconstを追加コンパイラあなたが従うことを意図した専門のパスに従います。

もちろん、closure_traits<decltype(thing)>::arityだけでなく、closure_traits<decltype(thing)>::arity::valueを印刷する必要があります。

+0

私はあなたが "専門化は*一致しない*一致"を意味すると思います – harmic

+0

ありがとう!それはうまくいった。 – sangrey

4

コンパイラが正しくラムダ型のoperator()を取得しますが、そのメンバ関数へのポインタがあるためconst修飾子のあなたの専門分野と一致していません。ケースの誰に

あなたは

template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>             
{ 
    // ... 
}; 

二分業を追加する必要があります(はい、関数の型を取るために、テンプレートを書くことは苦痛である)

+1

機能を持つテンプレートを書くことは苦痛ではありません。 typename呼び出し可能であり、残りのすべてについて心配する必要はありません。 typenameコンテナと同じです。 – rubenvb

1

は、次のように正しいコードはこのに問題があります。

#include<type_traits> 
#include<tuple>                   
#include<utility> 
#include<iostream>                          

/* For generic types use the type signature of their operator() */              
template <typename T>                      
struct closure_traits : public 
         closure_traits<decltype(&T::operator())> {};           

/* Otherwise, we do a template match on a function type. */ 
template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>             
{ 
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;            
    using Ret = ReturnType;         

    /* The argument types will be the same as the types of the 
    * elements of a tuple composed of them. 
    */                        
    template <std::size_t I>  
    struct Args {   
     using type = typename std::tuple_element<I, 
             std::tuple<ArgTypes...>>::type;                              
    };                                                           

};                                                             

int main() {                              
    auto thing = [=] (int x) {return x;}; 

    std::cerr << "The number of arguments is " 
       << closure_traits<decltype(thing)>::arity::value << std::endl;                              

    return 0;                              
}  
関連する問題