8

次の問題を解決するにはどうすればよいですか?

この質問に関連する次の関数を定義する関数ライブラリを作成しています。

  • call(f,arg): 引数を指定して関数を呼び出します。いくつかの状況で必要な単なるラッパーです。
  • comp(f1,f2): 2 つの関数の合成を返します。2 つの関数の構成を表すヘルパー ファンクターを返します。

実装は次のようになります (まだ問題を示す単純化されたバージョン)。

// Call f with one argument
template <class Fn, class Arg>
auto call(const Fn &f, const Arg & arg) -> decltype(f(arg)) {
    return f(arg);
}

// Helper functor for the function below
template<class Fn1, class Fn2>
class CompFn {
    Fn1 a;
    Fn2 b;

public:
    CompFn(const Fn1 &f1, const Fn2 &f2) : a(f1), b(f2) {}

    template<class Arg> inline
    auto operator()(const Arg & arg) const -> decltype(call(b, call(a, arg))) {
        return call(b, call(a, arg));
    }
};

/** Composition of f1 and f2 (f2 after f1). */
template<class Fn1, class Fn2>
CompFn<Fn1,Fn2> comp(const Fn1 &f1, const Fn2 &f2) {
    return CompFn<Fn1,Fn2>(f1, f2);
}

次のコードは、簡単なテストとして使用されます。

// Example: Take the length of the string and compare it against zero.
std::function<int(std::string)> stringLength = [](std::string s) { return s.size(); };
std::function<bool(int)> greaterZero = [](int x) { return x > 0; };
auto stringNotEmpty = comp(stringLength, greaterZero);

std::string testInput1 = "foo";
std::string testInput2 = "";

ここまでは、すべて正常に動作します。呼び出しcomp自体は問題ないようです。結果の関数を直接呼び出すこともできます。しかし、コンポジションを via で呼び出すと、call無数のコンパイル エラーが発生します (やあ、新記録!):

assert(call(stringNotEmpty,testInput1) == true);    // line 44
assert(call(stringNotEmpty,testInput2) == false);

コンパイル出力 (gcc 4.7、完全な出力は以下のアイデアリンクを参照):

prog.cpp:16:9: error: ‘std::function<bool(int)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::b’ is private
prog.cpp:44:5: error: within this context
prog.cpp:15:9: error: ‘std::function<int(std::basic_string<char>)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::a’ is private
prog.cpp:44:5: error: within this context
prog.cpp:22:10: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   [ skipping 890 instantiation contexts ]
[ ...continues endlessly... ]

コンポジションを に変換するときstd::functionも、まったく問題ありません。しかし、これは私の関数で多相ファンクターを使用することを許可しcompません。少なくともオプションはありません。

1 つの「修正」は、 のdecltypeで末尾Comp::operator()の戻り値の型を使用せず、戻り値の型をbool(この単一のテスト シナリオに特化した) に修正することです。

上記の 4 つのテスト ケースはすべて、次のようにまとめられています。

  • Test1 -- コンポジションを直接呼び出す --> OK
  • Test2 -- --> エラーを使用してコンポジションを呼び出しますcall
  • Test3 -- コンポジションを std::function にキャストし、次を使用して呼び出しますcall--> OK
  • Test4 - を使用してコンポジションを呼び出しcallます。Comp::operator()to bool--> OKの戻り値の型を修正

私の目標は、callファンクター、関数ポインター、メンバー関数ポインター、メンバー変数ポインターなど、あらゆるタイプの関数を呼び出す「シームレスな」ラッパーを作成することcompです。それらにはたくさんのオーバーロードがありますが、「再帰的な問題」のように思われるComp<Fn1,Fn2>ためFn1、オーバーロードを導入したくありません。Fn2

4

2 に答える 2

6

Clang は失敗したテスト ケースを問題なくコンパイルし、エラーは見られないので、これは GCC のバグだと思います。可能であれば、最小限の再現 (インクルードなし) でバグレポートを提出してください。

注: for についてcallは、既に標準の -にINVOKEたものがあります。これはマクロではなく、いわば概念です。などで使用されstd::bindstd::functionそのうちの 1 つが ですstd::reference_wrapper。これはstd::ref(fun)(args...)、 と同じことを達成できることを意味しますcall

于 2013-05-01T23:14:17.640 に答える
2

a を Fn1、b を Fn2 の式に置き換えて、プライベート メンバーに言及しないようにしてください。VC++ でこれを試しましたが、別のエラーが発生しました。

template<class Arg> inline
auto operator()(const Arg & arg) const -> decltype(call(Fn1(), call(Fn2(), arg))) {
    return call(b, call(a, arg));
}
于 2013-05-01T23:21:05.933 に答える