如果其中一个备选方案没有特定字段,则无法访问该变体

Unable to visit a variant if one of its alternatives does not have a specific field

我正在尝试访问包含多个 类 的变体。其中一个没有指定字段value,我用constexpr处理了,但是编译器还是编译不通过。

#include <variant>
#include <iostream>

struct A {};

struct B {
    int value = 1;
};

struct C {
    int value = 2;
};

int main() {

    const auto d = std::variant<A, B, C>{B{}};
    auto n = std::visit(
        [&](auto &data) -> int {
            if constexpr (std::is_same_v<decltype(data), A>) {
                return int{0};
            } else {
                return data.value;
            }
        },
        d);
    std::cout << n << std::endl;
    return 0;
}

错误:

error: ‘const struct A’ has no member named ‘value’
   22 |                 return data.value;

编译器版本:

clang --version
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

我是不是漏掉了什么或者设计上是不可能的?

编辑: 最短的解决方案是使用std::decay_t,但是,我不知道是否会有任何后果:

if constexpr (std::is_same_v<std::decay_t<decltype(data)>, A>)

您需要删除 data:

的引用和 cv-qualifiers
auto n = std::visit(
        [&](auto &data) -> int {
            if constexpr (std::is_same_v<std::remove_cvref_t<decltype(data)>, A>) {
                return int{0};
            } else {
                return data.value;
            }
        },
        d);

Demo

在C++20中,你可以只使用requires子句来检测data.value表达式是否有效。

const auto d = std::variant<A, B, C>{B{}};
    auto n = std::visit(
        [&](auto& data) -> int {
            if constexpr (requires { data.value; }) {
                return data.value;
            } else {
                return int{0};
            }
        },
        d);

或者,您可以按照 cppref 的建议使用 overloaded 助手。

#include <variant>
#include <iostream>

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

struct A {};

struct B {
    int value = 1;
};

struct C {
    int value = 2;
};

int main() {
    const auto d = std::variant<A, B, C>{B{}};
    auto n = std::visit(overloaded{
            [](auto const& data){return data.value;},
            [](A const&){return 0;}
        }, d);
    std::cout << n << '\n';
    return 0;
}