2

シンプルな ScopedExit クラスを実装しようとしています。コードは次のとおりです。

#include <iostream>
#include <functional>

template<class R, class... Args>
class ScopedExit
{
public:
    ScopedExit(std::function<R(Args...)> exitFunction)
    {
        exitFunc_ = exitFunction; 
    }

    ~ScopedExit()
    {
        exitFunc_();
    }
private:
    std::function<R(Args...)> exitFunc_;
};

template<>
class ScopedExit<void>
{
public:
    ScopedExit(std::function<void ()> exitFunction)
    {
        exitFunc_ = exitFunction; 
    }

    ~ScopedExit()
    {
        exitFunc_();
    }
private:
    std::function<void ()> exitFunc_;
};

void foo()
{
    std::cout << "foo() called\n";
}

class Bar
{
public:
    void BarExitFunc(int x, int y)
    {
        std::cout << "BarExitFunc called with x =" << x << "y = " << y << "\n";
    }
};

int main()
{
    Bar b;
    std::cout << "Register scoped exit func\n";
    {
        ScopedExit<void, int, int> exitGuardInner(std::bind(&Bar::BarExitFunc, &b, 18, 11));
    }
    ScopedExit exitGuardOutter(foo);
    std::cout << "About to exit from the scope\n";
    return 0;
}

したがって、いくつかの質問があります。

  1. exit の関数引数をそれに渡す方法は? たとえば、BarExitFunc を 18 と 11 の 2 つの整数引数にバインドします。では、デストラクタで exitFunc_ に渡すにはどうすればよいでしょうか? std::forward<> で関数を呼び出すようなものが必要だと思います。

  2. gcc 4.7.2 (ideone.com から) は、exitGuardOutter について文句を言います。それは言います:

prog.cpp:60:16: エラー: 'exitGuardOutter' の前にテンプレート引数がありません</p>

prog.cpp:60:16: エラー: 予想される ';' 「exitGuardOutter」の前</p>

前もって感謝します。

4

2 に答える 2

2

exit の関数引数をそれに渡す方法は? たとえば、BarExitFunc を 18 と 11 の 2 つの整数引数にバインドします。では、デストラクタで exitFunc_ に渡すにはどうすればよいでしょうか?

exitFunc_デストラクタで呼び出し時に引数を渡す理由がまったくわかりません。何をするにしても、ScopedExitコンストラクターでこれらの引数を前もって提供する必要があります。

最も簡単な方法は、既に行っているように、定義サイトで afunction<R()>bind必要な引数を使用するだけです。

ScopedExit<R> guard(std::bind(someFunction, someArg, otherArg));

これにより、可変個引数のテンプレート引数を完全に取り除くことができ、テンプレートが大幅に簡素化されます。


ここで、入力する必要があり、そのstd::bindような構文を使用したい場合は、次のようにします。

ScopedExit<R> guard(someFunction, someArg, otherArg);

本当に、テンプレートがより複雑になるので要点がわかりませんが、そうではありません...コンストラクター自体で引数をバインド/転送し、引き続き a を保存しfunction<R()>ます。

template<typename... Args>
ScopedExit(std::function<R(Args...)> exitFunction, Args&&... args)
{
    exitFunc_ = std::bind(exitFunction, std::forward<Args>(args)...); 
}

bindこれで、バインドする引数がなくても体系的に関数を処理できるようになったため、クラスを特殊化してbind、引数がないときにこれが役に立たないようにすることができます。これは練習問題として残します。


gcc 4.7.2 (ideone.com から) が exitGuardOutter について不平を言う

これは、fooが でなくstd::function、コンパイラが正しいテンプレート引数を推測できないためです。@ForEveR で既に述べたように、ガード変数を として定義するだけですScopedExit<void> guard(foo);

または、すべてをまとめて、最初に言ったこと (テンプレートから除外し、ガードの定義サイトで使用するのが最適です) を念頭に置いて、コンストラクターでbind取り除き、std::function任意のファンクターに一般化することができます (ところで、これは、 、ファンクター/コールバックが必要なときはいつでも標準ライブラリが行う方法です)。std::function<void()>非 void の戻り型も受け入れるため、ストレージの場合はそのまま使用できます。

class ScopedExit
{
public:
    template<typename Functor>
    ScopedExit(Functor exitFunction)
    {
        exitFunc_ = exitFunction; 
    }

    ~ScopedExit()
    {
        exitFunc_();
    }
private:
    std::function<void()> exitFunc_;
};

int foo() { return 0; }

struct Bar {
  void bye(int, int) {}
};

struct Baz {
  void operator ()() {}
};

int main() {
    const std::string what = "lambda!";
    ScopedExit guard1([&]() { std::cout << "yay a " << what << std::endl; });

    ScopedExit guard2(foo); // note how std::function<void()> accepts non-void return types

    Bar b;
    ScopedExit guard3(std::bind(&Bar::bye, &b, 1, 2));

    ScopedExit guard4(Baz());
}

元の可変個引数テンプレート クラスが、テンプレート引数が自動的に推定され、考えられるほぼすべての種類のファンクターを受け入れるテンプレート化されたコンストラクターだけを持つ柔軟な非テンプレート クラスになったことに注意してください。


注:これはデフォルトの引数では機能しないため、ほとんどすべてのファンクターを言いました:

void foobar(int = 0) {}
ScopedExit guard5(foobar); // error: too few arguments to function

Functora の代わりに a を直接格納したとしてもstd::function<void()>、デフォルトの引数を使用することはできません (の署名はfoobarデフォルトvoid(int)の引数でもあります)。何かのようなもの:

void foobar(int = 0) {}
ScopedExit guard5([]() { foobar(); });
于 2013-06-04T13:38:29.733 に答える
1

tuple1)たとえば、引数を保存できます。ただし、あなたの場合、引数を関数に既にバインドしているため、単に呼び出すことができexitFunc_()、関数定義は必要です。std::function<R()> exitFunctionこのようなものはおそらく

#include <iostream>
#include <functional>
#include <tuple>

template<size_t...>
struct indices {};
template<size_t N, size_t... Is>
struct gen_indices : gen_indices<N - 1, N - 1, Is...>
{
};
template<size_t... Is>
struct gen_indices<0, Is...> : indices<Is...>
{
};

template<class R, class... Args>
class ScopedExit
{
public:
    ScopedExit(std::function<R(Args...)> exitFunction, Args&&... args)
    : arguments_(std::forward_as_tuple(args...))
    {
        exitFunc_ = exitFunction;
    }

    ~ScopedExit()
    {
       call(gen_indices<sizeof...(Args)>());
    }
private:
    template<size_t... Idx>
    void call(indices<Idx...>)
    {
       exitFunc_(std::forward<Args>(std::get<Idx>(arguments_))...);
    }
    std::tuple<Args...> arguments_;
    std::function<R(Args...)> exitFunc_;
};

template<>
class ScopedExit<void>
{
public:
    ScopedExit(std::function<void ()> exitFunction)
    {
        exitFunc_ = exitFunction; 
    }

    ~ScopedExit()
    {
        exitFunc_();
    }
private:
    std::function<void ()> exitFunc_;
};

void foo()
{
    std::cout << "foo() called\n";
}

class Bar
{
public:
    void BarExitFunc(int x, int y)
    {
        std::cout << "BarExitFunc called with x =" << x << "y = " << y << "\n";
    }
};

int main()
{
    Bar b;
    std::cout << "Register scoped exit func\n";
    {
        ScopedExit<void, int, int> exitGuardInner
        (
           std::bind(&Bar::BarExitFunc, &b, std::placeholders::_1, 
           std::placeholders::_2), 10, 18
        );
    }
    ScopedExit<void> exitGuardOutter(foo);
    std::cout << "About to exit from the scope\n";
    return 0;
}

2) のように作成する必要がありますScopedExit<void>

于 2013-06-04T12:35:50.833 に答える