27

C++0x の関数または関数オブジェクトの引数と型に関する情報を抽出できるようにする特性クラスを作成しました (gcc 4.5.0 でテスト済み)。一般的なケースでは、関数オブジェクトを処理します。

template <typename F>
struct function_traits {
    template <typename R, typename... A>
    struct _internal { };

    template <typename R, typename... A>
    struct _internal<R (F::*)(A...)> {
        // ...
    };

    typedef typename _internal<decltype(&F::operator())>::<<nested types go here>>;
};

次に、グローバル スコープでプレーンな関数を特殊化します。

template <typename R, typename... A>
struct function_traits<R (*)(A...)> {
    // ...
};

これは正常に機能し、関数をテンプレートまたは関数オブジェクトに渡すことができ、適切に機能します。

template <typename F>
void foo(F f) {
    typename function_traits<F>::whatever ...;
}

int f(int x) { ... }
foo(f);

関数または関数オブジェクトを に渡す代わりにfoo、ラムダ式を渡したい場合はどうすればよいですか?

foo([](int x) { ... });

ここでの問題は、どちらの特殊化もfunction_traits<>当てはまらないことです。C++0x ドラフトでは、式の型は「一意で名前のない非共用体クラス型」であると述べています。式を呼び出した結果をデマングリングすると、構文的に C++ 型名を表すものではなくtypeid(...).name()、ラムダに対する gcc の内部命名規則のように見えるものが得られます。main::{lambda(int)#1}

要するに、ここのテンプレートに入れることができるものはありますか:

template <typename R, typename... A>
struct function_traits<????> { ... }

この特性クラスがラムダ式を受け入れることを許可しますか?

4

3 に答える 3

20

ラムダの特性を特殊化し、名前のないファンクターのシグネチャでパターン マッチングを行うことは可能だと思います。これは g++ 4.5 で動作するコードです。動作しますが、ラムダのパターン マッチングは直感に反して動作しているように見えます。インラインでコメントしました。

struct X
{
  float operator () (float i) { return i*2; }
  // If the following is enabled, program fails to compile
  // mostly because of ambiguity reasons.
  //double operator () (float i, double d) { return d*f; } 
};

template <typename T>
struct function_traits // matches when T=X or T=lambda
// As expected, lambda creates a "unique, unnamed, non-union class type" 
// so it matches here
{
  // Here is what you are looking for. The type of the member operator()
  // of the lambda is taken and mapped again on function_traits.
  typedef typename function_traits<decltype(&T::operator())>::return_type return_type;
};

// matches for X::operator() but not of lambda::operator()
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)> 
{
  typedef R return_type;
};

// I initially thought the above defined member function specialization of 
// the trait will match lambdas::operator() because a lambda is a functor.
// It does not, however. Instead, it matches the one below.
// I wonder why? implementation defined?
template <typename R, typename... A>
struct function_traits<R (*)(A...)> // matches for lambda::operator() 
{
  typedef R return_type;
};

template <typename F>
typename function_traits<F>::return_type
foo(F f)
{
  return f(10);
}

template <typename F>
typename function_traits<F>::return_type
bar(F f)
{
  return f(5.0f, 100, 0.34);
}

int f(int x) { return x + x;  }

int main(void)
{
  foo(f);
  foo(X());
  bar([](float f, int l, double d){ return f+l+d; });
}
于 2010-04-02T04:46:40.620 に答える
1

一部の作業をクラス テンプレートではなく一連の関数テンプレートに委任することで、関連情報を抽出できます。

ただし、最初に、関連するメソッドはconst、ラムダ (非キャプチャ、非ジェネリック、非mutableラムダ) のメソッドであると言わなければなりません。したがって、真のラムダとこれの違いを見分けることはできません。

struct {
    int operator() (int) const { return 7; }
} object_of_unnamed_name_and_with_suitable_method;

したがって、ラムダに対して「特別な扱い」をしたくない、型ラムダ型であるかどうかをテストしたくない、代わりに単に戻り型と型を抽出したいだけだと仮定する必要があります。十分に単純なオブジェクトのすべての引数。「十分に単純」とは、たとえば、operator()メソッド自体がテンプレートではないことを意味します。operator()そして、おまけ情報として、単純な古い関数とは対照的に、メソッドが存在して使用されたかどうかを示すブール値。



// First, a convenient struct in which to store all the results:
template<bool is_method_, bool is_const_method_, typename C, typename R, typename ...Args>
struct function_traits_results {
    constexpr static bool is_method = is_method_;
    constexpr static bool is_const_method = is_const_method_;
    typedef C class_type; // void for plain functions. Otherwise,
                          // the functor/lambda type
    typedef R return_type;
    typedef tuple<Args...> args_type_as_tuple;
};

// This will extract all the details from a method-signature:
template<typename>
struct intermediate_step;
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...)>  // non-const methods
    : public function_traits_results<true, false, C, R, Args...>
{
};
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...) const> // const methods
    : public function_traits_results<true, true, C, R, Args...>
{
};


// These next two overloads do the initial task of separating
// plain function pointers for functors with ::operator()
template<typename R, typename ...Args>
function_traits_results<false, false, void, R, Args...>
function_traits_helper(R (*) (Args...) );
template<typename F, typename ..., typename MemberType = decltype(&F::operator()) >
intermediate_step<MemberType>
function_traits_helper(F);


// Finally, the actual `function_traits` struct, that delegates
// everything to the helper
template <typename T>
struct function_traits : public decltype(function_traits_helper( declval<T>() ) )
{
};
于 2016-09-22T18:51:13.220 に答える