【发布时间】:2022-05-22 18:17:26
【问题描述】:
我正在尝试将仿函数移动到对象内的 lambda 中,如下所示:
#include <functional>
#include <iostream>
#include \"boost/stacktrace.hpp\"
#define fwd(o) std::forward<decltype(o)>(o)
struct CopyCounter {
CopyCounter() noexcept = default;
CopyCounter(const CopyCounter &) noexcept {
std::cout << \"Copied at\" << boost::stacktrace::stacktrace() << std::endl;
counter++;
}
CopyCounter(CopyCounter &&) noexcept = default;
CopyCounter &operator=(CopyCounter &&) noexcept = default;
CopyCounter &operator=(const CopyCounter &) noexcept {
std::cout << \"Copied at \" << boost::stacktrace::stacktrace() << std::endl;
counter++;
return *this;
}
inline static size_t counter = 0;
};
struct Argument : CopyCounter {};
struct Functor : CopyCounter {
int operator()(Argument) { return 42; }
};
template <class Result>
class Invoker {
std::function<void()> invoke_;
Result* result_ = nullptr;
template <class Functor, class... Args>
Invoker(Functor&& f, Args&&... args) {
if constexpr (std::is_same_v<Result, void>) {
invoke_ = [this, f = fwd(f), ... args = fwd(args)]() mutable {
f(fwd(args)...);
};
} else {
invoke_ = [this, f = fwd(f), ...args = fwd(args)]() mutable {
result_ = new Result(f(fwd(args)...));
};
}
}
template <class Functor, class... Args>
friend auto make_invoker(Functor&& f, Args&&... args);
public:
~Invoker() {
if (result_) delete result_;
}
};
template <class Functor, class... Args>
auto make_invoker(Functor&& f, Args&&... args) {
return Invoker<decltype(f(args...))>(fwd(f), fwd(args)...);
}
int main() {
Functor f;
Argument a;
auto i = make_invoker(std::move(f), std::move(a));
assert(CopyCounter::counter == 0);
return 0;
}
有点令人惊讶的是,最后一个断言在 libc++ 上失败,但在 libstdc++ 上没有。堆栈跟踪提示执行的两个副本:
Copied at 0# CopyCounter at /usr/include/boost/stacktrace/stacktrace.hpp:?
1# 0x00000000004C812E at ./src/csc_cpp/move_functors.cpp:38
2# std::__1::__function::__value_func<void ()>::swap(std::__1::__function::__value_func<void ()>&) at /usr/lib/llvm-10/bin/../include/c++/v1/functional:?
3# ~__value_func at /usr/lib/llvm-10/bin/../include/c++/v1/functional:1825
4# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
5# _start in ./bin/./src/csc_cpp/move_functors
Copied at 0# CopyCounter at /usr/include/boost/stacktrace/stacktrace.hpp:?
1# std::__1::__function::__value_func<void ()>::swap(std::__1::__function::__value_func<void ()>&) at /usr/lib/llvm-10/bin/../include/c++/v1/functional:?
2# ~__value_func at /usr/lib/llvm-10/bin/../include/c++/v1/functional:1825
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./bin/./src/csc_cpp/move_functors
在invoke_ 的移动赋值期间,似乎在库中仿函数和参数被复制到swap 中。
有两个问题:
- 为什么这是理想的行为?这个设计解决方案背后的动机是什么?
- 有什么好的方法可以更新代码以达到与 libstdc++ 中相同的语义?
-
请注意,我已经将 Boost.Stacktrace 分类为 an independent library,不需要 Boost。此外,如果您使用 C++23,您可以使用`std::stacktrace。
-
为什么不在 Invoker 的构造函数中使用初始化列表?
-
如果您已经在使用 lambda,为什么还要首先推出自己的仿函数类?
标签: c++ c++11 templates libstdc++ libc++