dla*_*ila 6 c++ lambda const c++14
我有以下api:
old_operation(stream, format, varArgs);
Run Code Online (Sandbox Code Playgroud)
我想写一个适配器,使编写调用成为可能,如下所示:
stream << operation(format, varArgs);
Run Code Online (Sandbox Code Playgroud)
为此,我使用一个临时对象,它存储对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);
}
Run Code Online (Sandbox Code Playgroud)
这很好但现在我需要添加一些using namespace嵌入到操作调用中的声明:
让我说我有
namespace X {namespace Y { namespace Z { int formater(double x) { return std::round(x); }}}
Run Code Online (Sandbox Code Playgroud)
我不想为这个调用添加所有名称空间,所以我做的事情如下:
#define OPERATION(...) \
[&]() { \
using namespace ::X:Y:Z; \
return Operation("" __VA_ARGS__); }() \
Run Code Online (Sandbox Code Playgroud)
这让我做:
stream << OPERATION(format, formater(2.3));
Run Code Online (Sandbox Code Playgroud)
lambda的问题在于临时创建的范围与Apply()调用不同,即UB.
我不知道,如果通过增加一个const限定mArgs提到它会延长记录文献的生活在这里.我不确定这是否适用,我假设它们是基于堆栈的引用,并且通过将const限定符添加到mArgs,限定符将应用于捕获的引用.
template<typename ...T>
decltype(auto) storage(T&& ...t) {
return [&](auto&& f) ->decltype(auto) {
return std::forward<decltype(f)>(f)(t...);
};
}
Run Code Online (Sandbox Code Playgroud)
这是一个 Haskell 风格的函子(嗯,是一个可变的函子,不是很 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));
};
}
Run Code Online (Sandbox Code Playgroud)
它返回一次性延迟调用std::apply。Apply 接受一个函数和一个元组,并将元组的参数传递给函数。它正确处理引用和右值/左值。同时,元组的容器使捕获变得更简单,并且让我们可以轻松地有条件地存储右值,同时将左值保留为引用。
我认为这解决了您的问题,因为临时数据被移入元组而不是通过引用捕获,而非临时数据则通过引用存储。
应该有std::experimental::apply比我在这里可以轻松绘制的任何实现更好的实现。