1

我正在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 发生?

4

1 回答 1

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();
    }
};
于 2020-03-04T11:33:52.327 回答