ScopedExit实现:向函数对象传递参数
ScopedExit implementation: pass arguments to the function object
我试图实现简单的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() calledn";
}
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 funcn";
{
ScopedExit<void, int, int> exitGuardInner(std::bind(&Bar::BarExitFunc, &b, 18, 11));
}
ScopedExit exitGuardOutter(foo);
std::cout << "About to exit from the scopen";
return 0;
}
这里有几个问题:
如何传递退出的函数参数给它?例如,我用两个整数参数绑定BarExitFunc: 18和11。那么我如何将它传递给析构函数中的exitFunc_呢?我想我需要一些类似于调用函数的std::forward<>
gcc 4.7.2 (from ideone.com)抱怨exitGuardOutter。它说:
prog.cpp:60:16:错误:在' exitGuardOutter '之前缺少模板参数
prog.cpp:60:16: error: expected '; ' before ' exitGuardOutter '
如何传递退出的函数参数给它?例如,我用两个整数参数绑定BarExitFunc: 18和11。那么我如何将它传递给析构函数中的exitFunc_呢?
我看不出有任何理由在析构函数调用时将参数传递给exitFunc_
。无论如何,您都必须在ScopedExit
构造函数中预先提供这些参数。
最直接的方法是简单地在定义站点使用function<R()>
和bind
任何必需的参数,就像你已经做的那样:
ScopedExit<R> guard(std::bind(someFunction, someArg, otherArg));
这使您可以完全摆脱可变模板参数,并大大简化您的模板。
现在,如果困扰您的是您必须键入std::bind
,并且您宁愿使用这样的语法:
ScopedExit<R> guard(someFunction, someArg, otherArg);
真的,我不明白这一点,因为它使模板更复杂,但为什么不…只需在构造函数中绑定/转发参数,并且仍然存储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 (from ideone.com)抱怨exitguardout
这是因为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
即使您直接存储Functor
而不是std::function<void()>
,您也无法使用默认参数(即使有默认参数,foobar
的签名仍然是void(int)
),所以您总是必须在定义站点处理这种角落情况,例如:
void foobar(int = 0) {}
ScopedExit guard5([]() { foobar(); });
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() calledn";
}
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 funcn";
{
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 scopen";
return 0;
}
2)应该像ScopedExit<void>
一样创建
- 如何使用Visual Studio 2017在C++中为参数化对象数组使用唯一指针
- Arduino C++在构造函数中用参数声明对象数组
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- C++ 类对象作为函数参数
- 如何将对象数组作为参数传递给模板
- 使用向量初始化参数化构造函数的对象数组
- 如何将成员函数作为参数传递并在派生对象上执行方法列表
- 如何编写将展开以定义具有模板参数的对象的宏
- C++类对象 - 遍历基于参数的对象
- 将指针传递到成员的指针,从模板参数包到函数对象
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 是否可以获取成员函数模板参数的拥有对象?
- C++多参数对象创建
- 在不创建参数对象的情况下解析constexpr函数
- 推断模板参数对象成员的类型
- 如何解析c++ v8中的参数对象
- 当默认参数对象被销毁时
- 为什么函数不能更改其参数对象的地址/引用值?