4

boost::functions の代わりとして独自のデリゲート システムを作成しようとしています。これは、boost::functions が多くのヒープ割り当てを行うため、問題があるとプロファイルしました。
私はこれを代わりに書きました(単純化して、実際のものはプールされたメモリと新しい配置を使用しますが、これはエラーを再現するのに十分簡単です):

template<class A, class B>
struct DelegateFunctor : public MyFunctor {
  DelegateFunctor(void (*fptr)(A, B), A arg1, B arg2) : fp(fptr), a1(arg1), a2(arg2) {}

  virtual void operator()() { fp(a1, a2); }

  void (*fp)(A, B);  // Stores the function pointer.
  const A a1; const B a2;  // Stores the arguments.
};

およびこのヘルパー関数:

template<class A, class B>
MyFunctor* makeFunctor(void (*f)(A,B), A arg1, B arg2) {
  return new DelegateFunctor<A,B>(f, arg1, arg2);
}

ここで奇妙なことが起こります:

void bar1(int a, int b) {
  // do something
}

void bar2(int& a, const int& b) {
  // do domething
}

int main() {
  int a = 0;
  int b = 1;

  // A: Desired syntax and compiles.
  MyFunctor* df1 = makeFunctor(&bar1, 1, 2);

  // B: Desired syntax but does not compile:
  MyFunctor* df2 = makeFunctor(&bar2, a, b);

  // C: Not even this:
  MyFunctor* df3 = makeFunctor(&bar2, (int&)a, (const int&)b);

  // D: Compiles but I have to specify the whole damn thing:
  MyFunctor* df4 = makeFunctor<int&, const int&>(&bar2, a, b);
}

バージョン C (B も同様) で発生するコンパイラ エラーは次のとおりです。

error: no matching function for call to ‘makeFunctor(void (*)(int&, const int&), int&, const int&)’

コンパイラがエラーメッセージで実際にタイプを正しく推定したため、これは奇妙です。

バージョン B をコンパイルする方法はありますか? boost::bind はこの制限をどのように回避しますか?
GCC 4.2.1 を使用しています。C++11 ソリューションはありません。

4

2 に答える 2

5

引数控除は参照を取り除きます。の関数ポインタ シグネチャを一致させることでAを取得したいのですint &が、実引数を一致させることで を取得したいintため、演繹は失敗します。

1 つの解決策は、次のように、2 番目のタイプを非推定にすることです。

#include <type_traits>

template <typename R, typename A, typename B>
R do_it(R (*func)(A, B),
        typename std::common_type<A>::type a,   // not deduced
        typename std::common_type<B>::type b)   // not deduced
{
    return func(a, b);
}

ABは、純粋に関数ポインタのシグネチャによって決定されます。

int foo(int &, int const &);

int main()
{
    int a = 0, b = 0;
    return do_it(foo, a, b);  // deduces A = int &, B = int const &
}

(std::common_type<T>::typeこれは、引数推定からテンプレート引数を削除することを唯一の目的とする「アイデンティティ型」の推奨イディオムであることに注意してください。これは、以前はidentity<T>::typeoralias<T>のように呼ばれていましたが、標準ライブラリの特性std::common_typeはこの目的をうまく果たします。)

于 2012-09-23T18:14:43.400 に答える
2

値の引数を使用してテンプレートの引数を推測する場合、値の型しか取得できません。つまり、次のような関数テンプレートを使用する場合

template <typename T>
void f(T) {
}

Tは常に非参照型になります。ここで、関数ポインターと値を渡そうとすると、関数が値の型をとらない場合、コンパイラーは推定された型を一貫させる方法がありません。

template <typename T>
void f(void (*)(T), T) {}

void f0(int);
void f1(int const&);

int main() {
    f(&f0, 0); // OK
    f(&f1, 0); // ERROR
}

この問題に対処する 1 つの方法は、対応する関数テンプレートを適切にオーバーロードすることです。以下の関数をミックスに追加すると、上記の例が再び機能します。

template <typename T>
void f(void (*)(T const&), T const&) {}

明らかに、これはすぐにメンテナンスの悪夢になり、おそらくやりたいことではありません別の方法は、それぞれの引数に異なるテンプレート パラメーターを使用することです。

template <typename T, typename S>
void f(void (*)(T), S) {}

これは機能しますが、2 番目の引数で本当に一致させたい型と必ずしも一致するとは限らないという直接的な影響があります。参照型を取得したい場合でも、値型になります (個人的には、しますが、これは別の問題です)。これが発生したくない場合は、コンパイラ形式がいくつかの引数の引数を推測しようとするのを防ぐことができます。例えば:

template <typename T>
struct helper {
    typedef T type;
};
template <typename T>
void f(void (*)(T), typename helper<T>::type) {}

上記の例は、テンプレート引数を 1 つだけ使用して目前の問題を示しているだけですが、これはより多くのテンプレート引数でも同様に機能すると確信しています。これが Boost の機能であるかどうかは、私にはわかりませんし、気にすることもありません。

于 2012-09-23T19:17:43.093 に答える