3

次の問題があります。ラムダ式を取ることができる汎用関数を書きたいとします。パラメータが std::function 型の場合、ラムダだけでなく、関数や関数へのポインタも使用できることを理解しています。そのため、最初のステップで、次のことを行いました。

void print(std::function<void(int, int)> fn) {
  fn(1,2);
}

int main() {

  print([](int i, int j) { std::cout << j <<','<<i<<'\n'; });
  return 0;
}

ここでの問題は、この関数をジェネリックにしたいということです。つまり、ラムダ式に 2 つのパラメーターしか持たせたくないということです。そこで、print 関数のシグネチャを次のようなより一般的なものに変更してみました。

template <class function_type>
void print(function_type fn);

しかし、今の問題は、関数が任意のオブジェクトを取ることであり、私はそれで大丈夫ではありません. しかし、主な問題は、オブジェクト fn が受け入れることができるパラメーターの数がわからないことです。

したがって、ある意味で、fn の引数の数を決定するコンパイル時の方法を探しています。可能であれば、fn の型を std::function に変更します。そして、fn が受け入れるパラメーターの数を知っているとすれば、fn に渡される任意の数のパラメーターをパックする一般的な方法はありますか? これが C++11 内で可能かどうかさえわかりません。つまり、引数の数が与えられた場合、パラメータをパックして fn に渡す方法はありますか? したがって、2 つの引数がある場合は、次のように呼び出します。

fn(arg1, arg2);

3 つある場合:

fn(arg1, arg2, arg3);

等々。

皆様、お知恵を貸していただきありがとうございます。

ああ

4

3 に答える 3

2

次のスニペットが役立つ場合があります。

std::functionこれにより、 aが取る引数の数が得られます

template <typename Signature>
struct count_args;

template <typename Ret, typename... Args>
struct count_args<std::function<Ret(Args...)>> {
    static constexpr size_t value = sizeof...(Args);
};

たとえば、次のコードはコンパイルされます (clang 3.2、gcc 4.7.2、および icc 13.1.0)。

static_assert(count_args<std::function<void()        >>::value == 0, "Ops!");
static_assert(count_args<std::function<void(int)     >>::value == 1, "Ops!");
static_assert(count_args<std::function<void(int, int)>>::value == 2, "Ops!");

私が理解している限り、正しい数の引数を渡して関数オブジェクトを呼び出したいと思いますよね? 次に、各引数に対して、その型に変換可能な値を提供する必要があります。この一般性による解決は非常に困難です (または不可能ですらあります)。したがって、2 つの代替案を提示します。

1各引数は、その型の値初期化オブジェクトです。(これはecatmur が提案したものです。)

template <typename Ret, typename... Args>
Ret call(const std::function<Ret(Args...)>& f) {
    return f(Args{}...); // for the intel compiler replace {} with ()
}

2固定値が与えられ、すべての引数はこの値から暗黙的に初期化されます。

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<sizeof...(Args) == sizeof...(Vals), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val&, const Vals&... vals) {
    return f(vals...);
}

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<(sizeof...(Args) > sizeof...(Vals)), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val& val, const Vals&... vals) {
    return call(f, val, val, vals...);
}

3 つのオーバーロードは明確であり、次の例に示すように使用できます。

{
    std::function<char()> f = []() -> char {
        std::cout << "f() ";
        return 'A';
    };
    std::cout << call(f)    << std::endl; // calls f()
    std::cout << call(f, 0) << std::endl; // calls f()
}
{
    std::function<char(int)> f = [](int i) -> char {
        std::cout << "f(" << i << ") ";
        return 'B';
    };
    std::cout << call(f)    << std::endl; // calls f(0)
    std::cout << call(f, 1) << std::endl; // calls f(1)
}
{
    std::function<char(int, int)> f = [](int i, int j) -> char {
        std::cout << "f(" << i << "," << j << ") ";
        return 'C';
    };
    std::cout << call(f)    << std::endl; // calls f(0, 0)
    std::cout << call(f, 2) << std::endl; // calls f(2, 2)
}
于 2013-04-13T19:42:46.310 に答える
0

callable のシグネチャを判断するには、「make_function」のラムダまたは任意の callable の呼び出しシグネチャを推測するのソリューションを使用できます。その後、callable を にパッケージ化するstd::functionか、タグを作成してパラメーターの推論を使用できます。

template<typename T> struct tag {};

template<typename F, typename... Args>
void print_impl(F &&fn, tag<void(Args...)>) {
  fn(Args{}...);
}

template<typename F>
void print(F &&fn) {
  print_impl(std::forward<F>(fn), tag<get_signature<F>>{});
}

これは値で初期化された引数を使用することに注意してください。もっと複雑なものが必要な場合は、タプルを「アンパック」するたびに呼び出して、一致する関数 pointer をstd::tuple<Args...>呼び出すことができます。

于 2013-04-04T09:34:43.943 に答える
0

fnはい、可変個引数テンプレートを使用して、必要な数のパラメーターをパックできます。

template <class function_type, class... Args>
void print(function_type fn, Args... args)
{
    //Call fn with args
    fn(std::forward<Args>(args...));
}

パラメーター パックに含まれる引数の数を調べるには、 を使用できますsizeof...(args)

于 2013-04-04T08:45:33.983 に答える