Вызов оператора << для типов, содержащихся в std :: варианте? - PullRequest
0 голосов
/ 19 апреля 2020

У меня есть такая структура:

// Literal.hpp
struct Literal
{
    std::variant<
        std::nullptr_t,
        std::string,
        double,
        bool
        >
        value;

    friend std::ostream &operator<<(std::ostream &os, Literal &literal);
};

, и я пытаюсь реализовать оператор << следующим образом: </p>

// Literal.cpp
Literal::Literal() : value(value) {}

std::ostream &operator<<(std::ostream &os, const Literal &literal)
{
    std::visit(/* I don't know what to put here!*/, literal.value);
}

Я пытался реализовать оператор вроде этого (примечание: я бы выбрал любое элегантное решение, которое не обязательно должно быть решением этой реализации ниже)

// In Literal.cpp
std::ostream &operator<<(std::ostream &out, const Literal literal)
{
    std::visit(ToString(), literal.value);
    return out;
}

struct ToString; // this declaration is in literal.hpp

void ToString::operator()(const std::nullptr_t &literalValue){std::cout << "null";}
void ToString::operator()(const char &literalValue){std::cout << std::string(literalValue);}
void ToString::operator()(const std::string &literalValue){std::cout << literalValue;}
void ToString::operator()(const double &literalValue){std::cout << literalValue;}
void ToString::operator()(const bool &literalValue){std::cout << literalValue;}

Но в моей основной функции передача литерала массива char не приводит к это в bool, когда он работает! игнорирование перегрузки оператора с использованием символа:

main() {
    Literal myLiteral;
    myLiteral.value = "Hello World";
    std::cout << myLiteral << std::endl;
}

1 Ответ

3 голосов
/ 19 апреля 2020

Это ошибка в вашей стандартной библиотеке. Предположительно, вы используете libstc ++ (стандартная библиотека GNU C ++), так как это то, что Godbolt показывает, как портит. Если вы компилируете с помощью libc ++ (стандартная библиотека C ++ Clang / LLVM), это работает как положено. Согласно странице cppreference std::vector<Types...>::operator=(T&& t), она

Определяет альтернативный тип T_j, который будет выбран по разрешению перегрузки для выражения F(std::forward<T>(t)), если бы было перегрузка мнимой функции F(T_i) для каждого T_i из типов ... в области действия одновременно, за исключением того, что:

  • Перегрузка F(T_i) рассматривается только в том случае, если объявление T_i x[] = { std::forward<T>(t) }; действителен для некоторой изобретенной переменной x;

  • Если T_i (возможно, cv-квалифицирован) bool, F(T_i) рассматривается только если std:remove_cvref_t<T> также bool.

Последний пункт есть для этой самой ситуации. Поскольку многие вещи могут быть преобразованы в bool, но мы обычно не предполагаем это преобразование, этот пункт приводит к тому, что выбираются последовательности преобразования, которые обычно не выбираются (char const* в bool является стандартным преобразованием, но для std::string «определяется пользователем», что обычно считается «хуже»). Ваш код должен установить value в альтернативу std::string, но реализация вашей библиотеки std::variant не работает. Возможно, билет на выпуск уже открыт, но если его нет, это является основанием для его открытия. Если вы застряли в своей библиотеке, явное пометить литерал как std::string должно работать:

literal.value = std::string("Hello World");

Для вопроса элегантности используйте сокращенный шаблон лямбда.

std::ostream &operator<<(std::ostream &os, Literal const &literal)
{
    std::visit([](auto v) { std::cout << v; }, literal.value);
    // or
    std::visit([](auto const &v) {
        // gets template param      vvvvvvvvvvvvvvvvvvvvvvvvv w/o being able to name it
        if constexpr(std::is_same_v<std::decay_t<decltype(v)>, std::nullptr_t>) {
           std::cout << "null";
        } else std::cout << v;
    }, literal.value);
    // only difference is nullptr_t => "nullptr" vs "null"

    return std::cout;
}

Кроме того, ваше объявление friend не соответствует определению. На самом деле, он не должен быть friend ed в любом случае, так как ему не нужен доступ к private членам.

// declaration in header, outside of any class, as a free function
std::ostream &operator<<(std::ostream&, Literal const&);
//                            was missing const ^^^^^
...