非递归访问自定义变体 - 如何优雅地返回值?

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::visitif...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();
    
;

【讨论】:

这很聪明,但并没有真正解决原来的问题。您仍然需要一个缓冲区来存储结果,并且还有额外的检查和额外的移动构造函数调用。

以上是关于非递归访问自定义变体 - 如何优雅地返回值?的主要内容,如果未能解决你的问题,请参考以下文章

python 复习 4-1 函数参数返回值递归

java基础3-重载+命令行传参+递归+数组+排序

16.如何优雅地获取跨层级组件实例(拒绝递归)

如何稍微优雅滴完成博文访问计数[SpringBoot+redis+分布式锁]

如何稍微优雅滴完成博文访问计数[SpringBoot+redis+分布式锁]

Shell中处理方法返回值问题