fsquirrel
fsquirrel

Reputation: 832

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

I am trying to visit a variant containing several classes. One of them does not have a specific field value but I handle it with constexpr, however, the compiler still fails to compile.

#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:

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

Compiler version:

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

Do I miss something or it is impossible by design?

EDIT: The shortest solution was to use std::decay_t, however, I don't know if there can be any consequences:

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

Upvotes: 2

Views: 129

Answers (2)

JHBonarius
JHBonarius

Reputation: 11271

Alternatively, you could use the overloaded helper, as suggested by cppref.

#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;
}

Upvotes: 1

康桓瑋
康桓瑋

Reputation: 42756

You need remove reference and cv-qualifiers of the data:

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

In C++20, you can just use the requires clause to detect whether the data.value expression is valid.

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);

Upvotes: 4

Related Questions