我有以下api:
old_operation(stream, format, varArgs);
我想写一个适配器,可以按如下方式编写调用:
stream << operation(format, varArgs);
为此,我使用一个临时对象存储对varArgs的引用,并重载operator<<
以应用old_operation()
,如下所示:
template<typename ...T>
decltype(auto) storage(T&& ...t) {
return [&](auto&& f) ->decltype(auto) {
return std::forward<decltype(f)>(f)(t...);
};
}
template<typename ...T>
class Operation
{
using Storage = decltype(storage(std::declval<T>()...));
public:
template<typename ...Args>
explicit Operation(Args&& ...args) :
mArgs(storage(std::forward<Args>(args)...)) {};
template<class StreamType>
StreamType& Apply(StreamType& stream)
{
auto f = [&](auto&& ...xs)
{
old_operation(stream, std::forward<decltype(xs)>(xs)...);
}
mArgs(f);
return stream;
}
private:
Storage mArgs;
};
template<typename ...Args>
Operation<Args...> MakeOperation(Args&&... args)
{
return Operation<Args...>(std::forward<Args>(args)...);
}
template<class StreamType, typename ...Args>
StreamType& operator<<(StreamType& stream, Operation<Args...>&& operation)
{
return operation.Apply(stream);
}
这很好但现在我需要在操作调用中添加一些using namespace
声明:
让我说我有
namespace X {namespace Y { namespace Z { int formater(double x) { return std::round(x); }}}
而且我不想为此调用添加所有命名空间,所以我正在做类似的事情:
#define OPERATION(...) \
[&]() { \
using namespace ::X:Y:Z; \
return Operation("" __VA_ARGS__); }() \
允许我这样做:
stream << OPERATION(format, formater(2.3));
lambda的问题在于临时创建的范围不同于Apply()
调用,即UB。
我不知道是否通过向mArgs添加const限定符,它将延长所提到的here所捕获的引用的生命周期。我不确定这是否适用,我假设它们是基于堆栈的引用,并且通过将const限定符添加到mArgs,限定符将应用于捕获的引用。
答案 0 :(得分:4)
template<typename ...T>
decltype(auto) storage(T&& ...t) {
return [&](auto&& f) ->decltype(auto) {
return std::forward<decltype(f)>(f)(t...);
};
}
这是一个haskell风格的仿函数(好吧,一个变量函数,它不是很好的)。它需要Ts...
并返回类型((Ts...)->U)->U
的函数,即知道如何评估传递给它的参数的函数。这使storage
类型为(Ts...)->( ((Ts...)->U)->U )
,以获得一些代数乐趣。
我怀疑你的问题是你有没有存储的临时工。通常不存储传递给函数的临时值,其中返回值取决于那些临时值的生命周期,导致代码脆弱。
如果您有C ++ 1z experimental::apply
,我们可以这样做:
template<class... Ts>
decltype(auto) storage(Ts&&... ts) {
return
[tup=std::tuple<Ts...>(std::forward<Ts>(ts)...)]
(auto&& f)
->decltype(auto) mutable
{
return std::experimental::apply(decltype(f)(f), std::move(tup));
};
}
会向std::apply
返回一次性延迟通话。 Apply接受一个函数和一个元组,并将元组的参数传递给函数。它正确处理参考和r / lvalue-ness。同时,元组的容器使捕获更简单,并允许我们轻松有条件地存储rvalues,同时保持左值作为参考。
我认为这可以解决你的问题,因为临时工具被移入元组而不是被引用捕获,而非临时存储则通过引用存储。
应该有std::experimental::apply
个实现比我在这里可以轻松绘制的任何内容都要好。