非递归访问自定义变体 - 如何优雅地返回值?
Posted
技术标签:
【中文标题】非递归访问自定义变体 - 如何优雅地返回值?【英文标题】:Non-recursively visit a custom variant - how to elegantly return a value? 【发布时间】:2020-03-04 10:37:18 【问题描述】:我正在为个人项目和学习体验编写std::variant
的准系统版本。我要实现的访问策略是if...else if
链,而不是constexpr
函数指针表。原因是后者对于编译器来说是出了名的难以优化,并且很容易产生一个基准,其中std::visit
被if...else if
链击败。
我正在尝试使用 折叠表达式 来实现它,但是当找到正确的访问者时,我找不到返回值的方法。这是我目前所拥有的:
template <typename... Ts>
struct my_variant
std::byte _buffer[std::max(sizeof(Ts)...)];
std::size_t _discriminator;
// ...
auto match(auto&&... fs)
overload_set matcher(std::forward<Fs>(fs)...);
[&]<std::size_t... Is>(std::index_sequence<Is...>)
([&]
if (_discriminator == Is)
// How to return from here?
matcher(*reinterpret_cast<Ts *>(&_buffer));
(), ...);
(std::make_index_sequence_for<Ts...>);
;
我目前的策略是为变体中的所有类型创建一个std::index_sequence
,然后折叠逗号运算符以使编译器生成一堆if
语句。由于if
不是一个表达式,我不得不将它包装成一个lambda 表达式 以便能够折叠它。如果我尝试返回,我将从 lambda 本身返回,并且不会传播到上层。
我可以使用缓冲区来存储结果,然后将其返回,但这违背了目的,因为它会阻止 RVO。
有没有一种方法可以让我以非递归方式编写 match
并且仍然返回访问者的结果以允许 RVO 发生?
【问题讨论】:
我想你想要this approach。 【参考方案1】:您需要选择一个不会丢弃该值的运算符。
template <typename T>
struct result // aka std::optional
std::aligned_storage_t<T> store;
bool has_value;
result() : has_value(false)
result(T t) : new(store) T(std::move(t)), has_value(true)
const result & operator| (const result & other) const return has_value ? *this : other;
T get() return std::move(*reinterpret_cast<T *>(store));
;
template <typename... Ts>
struct my_variant
std::byte _buffer[std::max(sizeof(Ts)...)];
std::size_t _discriminator;
// ...
auto match(auto&&... fs)
overload_set matcher(std::forward<Fs>(fs)...);
using result_t = result<std::common_type_t<std::invoke_result_t<matcher, Ts>...>>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>)
return ([&]() -> result_t
if (_discriminator == Is)
// How to return from here?
return matcher(*reinterpret_cast<Ts *>(&_buffer));
return result_t;
() | ...);
(std::make_index_sequence_for<Ts...>).get();
;
【讨论】:
这很聪明,但并没有真正解决原来的问题。您仍然需要一个缓冲区来存储结果,并且还有额外的检查和额外的移动构造函数调用。以上是关于非递归访问自定义变体 - 如何优雅地返回值?的主要内容,如果未能解决你的问题,请参考以下文章
如何稍微优雅滴完成博文访问计数[SpringBoot+redis+分布式锁]