10

型 F が形式の関数呼び出し演算子を持っている場合にのみ、beis_callable<F, Arg>と定義する C++ メタ関数を書きたいと思います。たとえば、次の場合valuetrueSomeReturnType operator()(const Arg &)

struct foo {
  void operator(const int &) {}
};

私はなりたいとis_callable<foo, int &>思います。これは私がこれまでに持っているものです:falseis_callable<foo, const int &>true

#include <memory>
#include <iostream>

template<typename F, typename Arg>
struct is_callable {
private:

  template<typename>
  static char (&test(...))[2];

  template<unsigned>
  struct helper {
    typedef void *type;
  };

  template<typename UVisitor>
  static char test(
               typename helper<
                 sizeof(std::declval<UVisitor>()(std::declval<Arg>()), 0)
                 >::type
               );
public:
  static const bool value = (sizeof(test<F>(0)) == sizeof(char));
};

struct foo {
  void operator()(const int &) {}
};

using namespace std;

int main(void)
{
  cout << is_callable<foo, int &>::value << "\n";
  cout << is_callable<foo, const int &>::value << "\n";

  return 0;
}

これは1andを出力しますが、 andのみが定義されているため、 and1が必要です。01foovoid operator()(const int &)

4

6 に答える 6

9

C++ チャット ルームで何時間も遊んで真剣に議論した後operator()、@KerrekSB と @BenVoigt のバージョンに基づいて、オーバーロードまたは継承された可能性のあるファンクターと関数ポインターで動作するバージョンを最終的に取得しました。

#include <utility>
#include <type_traits>

template <typename F, typename... Args>
class Callable{
  static int tester[1];
  typedef char yes;
  typedef yes (&no)[2];

  template <typename G, typename... Brgs, typename C>
  static typename std::enable_if<!std::is_same<G,C>::value, char>::type
      sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (C::*pfn)(Brgs...));

  template <typename G, typename... Brgs, typename C>
  static typename std::enable_if<!std::is_same<G,C>::value, char>::type
      sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (C::*pfn)(Brgs...) const);

  template <typename G, typename... Brgs>
  static char sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (G::*pfn)(Brgs...));

  template <typename G, typename... Brgs>
  static char sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (G::*pfn)(Brgs...) const);

  template <typename G, typename... Brgs>
  static yes test(int (&a)[sizeof(sfinae<G,Brgs...>(&G::operator()))]);

  template <typename G, typename... Brgs>
  static no test(...);

public:
  static bool const value = sizeof(test<F, Args...>(tester)) == sizeof(yes);
};

template<class R, class... Args>
struct Helper{ R operator()(Args...); };
 
template<typename R, typename... FArgs, typename... Args>
class Callable<R(*)(FArgs...), Args...>
  : public Callable<Helper<R, FArgs...>, Args...>{};

Ideone での実例。失敗した 2 つのテストはオーバーロードされたテストであることに注意してくださいoperator()。これは可変個引数テンプレートに関する GCC のバグで、GCC 4.7 で既に修正されています。Clang 3.1 は、すべてのテストを として報告しますpassed

デフォルトの引数で失敗させたい場合operator()は、それを行う方法がありますが、その時点で他のいくつかのテストが失敗し始め、それを試して修正するのは面倒であることがわかりました。

編集: @Johannes がコメントで正しく指摘しているように、ここで少し矛盾がありました。つまり、関数ポインターへの変換を定義するファンクターは「呼び出し可能」として検出されません。これは、私見ですが、修正するのはかなり簡単ではないため、(今のところ)気にしません。この特性が絶対に必要な場合は、コメントを残してください。何がで​​きるか見てみましょう.


これがすべて言われたので、IMHO、この特性のアイデアはばかげています。なぜあなたはそのような正確な要件を持っているのですか? 基準is_callableが十分でないのはなぜですか?

(はい、そのアイデアはばかげていると思います。はい、私はまだ行ってこれを構築しました。はい、とても楽しかったです。いいえ、私は正気ではありません。少なくともそれは私が信じていることです...)

于 2012-01-18T08:57:26.907 に答える
7

(彼の答えを出発点として使用したことについてケレックに謝罪します)

編集: 型をまったく処理しないように更新しましたoperator()

#include <utility>

template <typename F, typename Arg>
struct Callable
{
private:
  static int tester[1];
  typedef char                      yes;
  typedef struct { char array[2]; } no;

  template <typename G, typename Brg>
  static char sfinae(decltype(std::declval<G>()(std::declval<Brg>())) (G::*pfn)(Brg)) { return 0; }

  template <typename G, typename Brg>
  static char sfinae(decltype(std::declval<G>()(std::declval<Brg>())) (G::*pfn)(Brg) const) { return 0; }

  template <typename G, typename Brg>
  static yes test(int (&a)[sizeof(sfinae<G,Brg>(&G::operator()))]);

  template <typename G, typename Brg>
  static no test(...);

public:
  static bool const value = sizeof(test<F, Arg>(tester)) == sizeof(yes);
};

struct Foo
{
  int operator()(int &) { return 1; }

};

struct Bar
{
  int operator()(int const &) { return 2; }
};

struct Wazz
{
  int operator()(int const &) const { return 3; }
};

struct Frob
{
  int operator()(int &) { return 4; }
  int operator()(int const &) const { return 5; }
};

struct Blip
{
  template<typename T>
  int operator()(T) { return 6; }
};

struct Boom
{

};

struct Zap
{
  int operator()(int) { return 42; }
};

#include <iostream>
int main()
{
  std::cout << "Foo(const int &):  " << Callable<Foo,  int const &>::value << std::endl
            << "Foo(int &):        " << Callable<Foo,  int &>::value << std::endl
            << "Bar(const int &):  " << Callable<Bar,  const int &>::value << std::endl
            << "Bar(int &):        " << Callable<Bar,  int &>::value << std::endl
            << "Zap(const int &):  " << Callable<Zap , const int &>::value << std::endl
            << "Zap(int&):         " << Callable<Zap , int &>::value << std::endl
            << "Wazz(const int &): " << Callable<Wazz, const int &>::value << std::endl
            << "Wazz(int &):       " << Callable<Wazz, int &>::value << std::endl
            << "Frob(const int &): " << Callable<Frob, const int &>::value << std::endl
            << "Frob(int &):       " << Callable<Frob, int &>::value << std::endl
            << "Blip(const int &): " << Callable<Blip, const int &>::value << std::endl
            << "Blip(int &):       " << Callable<Blip, int &>::value << std::endl
            << "Boom(const int &): " << Callable<Boom, const int &>::value << std::endl
            << "Boom(int&):        " << Callable<Boom, int &>::value << std::endl;
}

デモ: http://ideone.com/T3Iry

于 2012-01-18T04:04:17.027 に答える
2

テンプレートが:でインスタンス化されているかどうかを確認するために追加のテストを利用する可能な解決策は次のconst T&とおりです。

#include <memory>
#include <iostream>

using namespace std;

template<typename F, typename Arg>
struct is_callable {
private:

  template<typename>
  static char (&test(...))[2];

  template<bool, unsigned value>
  struct helper {};

  template<unsigned value>
  struct helper<true, value> {
    typedef void *type;
  };

  template<typename T>
  struct is_const_ref {};

  template<typename T>
  struct is_const_ref<T&> {
    static const bool value = false;
  };

  template<typename T>
  struct is_const_ref<const T&> {
    static const bool value = true;
  };

  template<typename UVisitor>
  static char test(typename helper<is_const_ref<Arg>::value, 
                                   sizeof(std::declval<UVisitor>()(std::declval<Arg>()), 0)>::type);
public:
  static const bool value = (sizeof(test<F>(0)) == sizeof(char));
};

struct foo {
  void operator()(const int &) {}
};

int main(void)
{
  cout << is_callable<foo, int &>::value << "\n";
  cout << is_callable<foo, const int &>::value << "\n";

  return 0;
}
于 2012-01-18T04:10:08.667 に答える
2

これが私がハックしたもので、あなたが必要としているものかもしれないし、そうでないかもしれません。それは...に真(偽)を与えるようです(const) int &...

#include <utility>

template <typename F, typename Arg>
struct Callable
{
private:
  typedef char                      yes;
  typedef struct { char array[2]; } no;

  template <typename G, typename Brg>
  static yes test(decltype(std::declval<G>()(std::declval<Brg>())) *);

  template <typename G, typename Brg>
  static no test(...);

public:
  static bool const value = sizeof(test<F, Arg>(nullptr)) == sizeof(yes);
};

struct Foo
{
  int operator()(int &) { return 1; }
  // int operator()(int const &) const { return 2; } // enable and compare
};

#include <iostream>
int main()
{
  std::cout << "Foo(const int &): " << Callable<Foo, int const &>::value << std::endl
            << "Foo(int &):       " << Callable<Foo, int &>::value << std::endl
    ;
}
于 2012-01-18T03:14:24.390 に答える
2

もしかしてこういうこと?VS2010で動作させるのは少し面倒です。

template<typename FPtr>
struct function_traits_impl;

template<typename R, typename A1>
struct function_traits_impl<R (*)(A1)>
{
    typedef A1 arg1_type;
};

template<typename R, typename C, typename A1>
struct function_traits_impl<R (C::*)(A1)>
{
    typedef A1 arg1_type;
};

template<typename R, typename C, typename A1>
struct function_traits_impl<R (C::*)(A1) const>
{
    typedef A1 arg1_type;
};

template<typename T>
typename function_traits_impl<T>::arg1_type arg1_type_helper(T);

template<typename F>
struct function_traits
{
    typedef decltype(arg1_type_helper(&F::operator())) arg1_type;
};

template<typename F, typename Arg>
struct is_callable : public std::is_same<typename function_traits<F>::arg1_type, const Arg&>
{
}
于 2012-01-18T01:54:02.963 に答える
0

他のことをしているときにこれに遭遇し、コードを適合させることができました。@Xeo と同じ機能 (および制限) がありますが、sizeof trick/enable_if は必要ありません。デフォルトのパラメーターは、テンプレート関数を処理するために enable_if を実行する必要がある代わりに使用されます。Xeoが書いた同じテストコードを使用して、g ++ 4.7およびclang 3.2でテストしました

#include <type_traits>
#include <functional>

namespace detail {
  template<typename T, class Args, class Enable=void>
  struct call_exact : std::false_type {};

  template<class...Args> struct ARGS { typedef void type; };

  template<class T, class ... Args, class C=T>
  C * opclass(decltype(std::declval<T>()(std::declval<Args>()...)) (C::*)(Args...)) { }
  template<class T, class ... Args, class C=T>
  C * opclass(decltype(std::declval<T>()(std::declval<Args>()...)) (C::*)(Args...) const) { }

  template<typename T, class ... Args>
  struct call_exact<T, ARGS<Args...>,
    typename ARGS<
       decltype(std::declval<T&>()(std::declval<Args>()...)),
       decltype(opclass<T, Args...>(&T::operator()))
     >::type
   > : std::true_type {};
}

template<class T, class ... Args>
struct Callable : detail::call_exact<T, detail::ARGS<Args...>> { };

template<typename R, typename... FArgs, typename... Args>
struct Callable<R(*)(FArgs...), Args...>
 : Callable<std::function<R(FArgs...)>, Args...>{};
于 2013-03-28T23:52:36.853 に答える