4

コンパイラが引数を調べた後に選択する、オーバーロードされた関数への関数ポインタを取得するにはどうすればよいですか? この例では:

#include <iostream>

void MyFunction(float a){}
void MyFunction(int a){}

int main()
{
  float a;

  MyFunction(a);

  void (*manualFunctionPointer)(float);
  manualFunctionPointer(a);

  // automaticFunctionPointer = ?
}

float を受け入れて void を返す関数への関数ポインタが必要であることを指定しました。MyFunction(a) 呼び出しが適切な関数を呼び出すため、コンパイラは確かにそれを自分で把握できます。コンパイラが選択した関数への関数ポインタを取得する方法はありますか?

4

3 に答える 3

2
#include <iostream>

void MyFunction(float a){std::cout << "float\n";}
void MyFunction(int a){std::cout << "int\n";}

template<typename Func, typename T>
void Do( Func f, T t )
{
    f(t);
}
template<typename T>
void DoMyFunction( T t )
{
  Do(static_cast<void(*)(T)>(MyFunction), t);
}
template<typename T>
void DoSomeFunction( T t, void(*func)(T) )
{
  Do(func, t);
}
int main()
{
  float a;

  MyFunction(a);

  void (*manualFunctionPointer)(float) = MyFunction;
  manualFunctionPointer(a);

//  Do(MyFunction, a); -- does not compile
  Do(static_cast<void(*)(float)>(MyFunction), a);
  DoMyFunction(a);
  DoSomeFunction(a, MyFunction);
}

上記の作品。4 つの異なる方法で MyFunction を選択しました。

ボイラーレートを実行する意思があり、「a が a である場合」の問題を解決したい場合は、次のchar方法が役立ちます。

// wrap the idea of calling MyFunction in a type:
struct MyFunctionFunctor {
  template<typename T> static auto Do( T&& t )->decltype(MyFunction(std::forward(t))) {
    return MyFunction(std::forward(t));
  }
};
// Calling MyFunctionFunctor::Do( x ) will basically do static dispatch on all of
// the overloads of MyFunction

// wrap the idea of dispatching a variable to a functor:
template<typename T, typename Functor>
struct Dispatch {
  static auto Do( T t )->decltype( Functor::Do( t ) )
  {
    return Functor::Do( t );
  }
}

int main()
{
  char a;
  auto func_ptr = &Dispatch<decltype(a), MyFunctionFunctor>::Do;
  func_ptr(a);
}

ただし、前述のように、これMyFunctionには型で記述されるようにまとめる必要があります。ボイラープレートなしでこれを行う方法を知りません。

于 2012-11-29T18:59:31.213 に答える
1

編集:これは実際には解決策ではありません。それはもっとチートです。でも、必要なことはしてくれていると思います。

#define INVOKE(hof, func, arg) \
   hof([](const decltype(arg)& arg_){return func(arg_);}, arg)

例:

// This function mimics the signature of QtCollector::run for testing
template <typename Functor, typename Arg1>
auto QtConcurrent_run(Functor functor, const Arg1 &arg1)
      -> decltype(functor(arg1))
{
      return functor(arg1);
}

#include <iostream>

int f(int x) { std::cout << "int" << " " << x << std::endl; return x; }
double f(double x) { std::cout << "double" << " " << x << std::endl; return x; }

int main() {
  INVOKE(QtConcurrent_run, f, 3);
  INVOKE(QtConcurrent_run, f, 3.14);
  INVOKE(QtConcurrent_run, f, '3');
  return 0;
}

こちらのideoneでご覧ください。

歴史的な目的と少しの説明のために、元の答えが続きます。


わかりやすくするために、これは興味深い質問ですが、おそらくプロジェクトを動かすことがより重要であるため、さまざまな関数オーバーライドをファンクター構造体にラップしてファンクターを渡したくない理由はありますかstruct を QtConcurrent::run に直接渡しますが、そのようなことを喜んで受け入れるのはどれでしょうか?

関数のすべての定義が単一のクラスにある場合、問題はありません。

struct f_collector {
  ReturnType1 f(ArgType1 arg);
  ReturnType2 f(ArgType2 arg);
  ReturnType3 f(ArgType3 arg);
  // ...
  // Make it a functor:
  template<typename Argtype>
  auto operator()(const Argtype& arg) -> decltype(f(arg)) { return f(arg); }
}

その後、電話QtConcurrent::run(f_collector(), argument)をかければ問題なく機能します (完全な転送が必要な場合を除きますが、それは些細なことです)。

そこで、上記のようなファンクターをオンザフライで構築するという次のアイデアがありました。これは基本的に、それにラムダ式を与えることを意味します。ラムダ自体は簡単なボイラープレートです。それからマクロを作成するのに十分簡単です:

// This is the functor
template<typename Arg, typename Func> struct wrapper {
  wrapper(Func f) : f(f) {}
  const Func f;
  auto operator()(Arg arg) const -> decltype(f(arg)) {return f(arg);}
};

// As usual, a make_* function, because you can't template deduce a constructor
template<typename Arg, typename Func>
wrapper<Arg, Func> make_wrapper(Func f) {
  return wrapper<Arg, Func>(f);
}

// Boilerplate inside a macro
#define INVOKE(hof,func,arg) \
   hof(make_wrapper<decltype(arg)>( [](const decltype(arg)& arg_) { \
                                      return func(arg_); \
                                    }), \
       arg) 

// The above was ugly, but it's easy to use. For testing, I define
// this with a similar signature to QtConcurrent::run
template <typename Functor, typename Arg1>
auto QtConcurrent_run(Functor functor, const Arg1 &arg1)
      -> decltype(functor(arg1))
{
      return functor(arg1);
}

#include <iostream>

int f(int x) { std::cout << "int" << " " << x << std::endl; return x; }
double f(double x) { std::cout << "double" << " " << x << std::endl; return x; }


int main() {
  INVOKE(QtConcurrent_run, f, 3);
  INVOKE(QtConcurrent_run, f, 3.14);
  INVOKE(QtConcurrent_run, f, '3');
  return 0;
}

しかし、ラムダは、他の美徳とともに、キャプチャがない限り、自動的に関数ポインターに変換できることを思い出しました。そして、このラムダにはキャプチャがありません。唯一の外部シンボルは関数自体であり、それは自動ストレージ クラスを持つオブジェクトではないためです。つまり、要するに、実際には少し定型文でこれを行うことができるということです。

#define INVOKE(hof, func, arg) \
   hof([](const decltype(arg)& arg_){return func(arg_);}, arg);
于 2012-11-30T00:37:15.463 に答える
0

フロートを交換してください

void (*manualFunctionPointer)(float);

void (*manualFunctionPointer)(decltype(a));

その後、変更が何であれ、manualFunctionPointerが続きます

于 2012-11-29T19:01:17.627 に答える