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. 如何将出口的函数参数传递给它?例如,我用两个整数参数绑定 BarExitFunc:18 和 11。那么如何将它传递给析构函数中的 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

如何将出口的函数参数传递给它?例如,我用两个整数参数绑定 BarExitFunc:18 和 11。那么如何将它传递给析构函数中的 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

这是因为foois not astd::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

即使您Functor直接存储 a 而不是 astd::function<void()>您也无法使用默认参数(即使带有默认参数的签名foobar仍然void(int)是),因此始终必须在定义站点处理这种极端情况就像是:

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

1)例如,您可以保存参数tuple。但是,在您的情况下,您可以简单地调用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 回答