呼叫<<在std :: variant中持有的类型上的运算符? [英] Calling << operator on types held in a std::variant?

查看:120
本文介绍了呼叫<<在std :: variant中持有的类型上的运算符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个这样的结构:

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

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

并且我正在尝试实现<<像这样的运算符:

and I'm trying to implement the << operator like this:

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

我已经尝试过像这样实现运算符(请注意:我会采用任何优雅的解决方案,它不一定是下面的实现的解决方案)

I've tried implementing the operator like this (note: I would take any elegant solution it doesn't have to be a solution to this implementation below)

// 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数组文字并不会在运行时将其转换为布尔值!忽略运算符重载,取一个字符:

But in my main function, passing a char array literal doesn't casts it into a bool when it runs! ignoring the operator overload taking a char:

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

推荐答案

这是标准库中的错误.大概您正在使用libstc ++(GNU C ++标准库),因为这就是Godbolt所显示的混乱.如果使用libc ++(Clang/LLVM的C ++标准库)进行编译,则可以按预期工作.根据 std::vector<Types...>::operator=(T&& t)的cppreference页面,它

This is a bug in your standard library. Presumably you're using libstc++ (the GNU C++ standard library), since that's what Godbolt shows as messing up. If you compile with libc++ (Clang/LLVM's C++ standard library), this works as expected. According to std::vector<Types...>::operator=(T&& t)'s cppreference page, it

确定范围中的每个T_i同时存在虚函数F(T_i)的重载时,由F(std::forward<T>(t))的重载解析选择的替代类型T_j ,除了:

Determines the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types... in scope at the same time, except that:

  • 仅当声明T_i x[] = { std::forward<T>(t) };对某些发明变量x有效时,才考虑重载F(T_i);

  • An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable x;

如果T_i是(cv限定)bool,则仅当std:remove_cvref_t<T>也是bool时才考虑F(T_i).

If T_i is (possibly cv-qualified) bool, F(T_i) is only considered if std:remove_cvref_t<T> is also bool.

在这种情况下,最后一个子句在那里.因为很多东西都可以转换为bool,但是我们通常不打算进行此转换,所以该子句会导致通常不会选择的转换顺序被选中(char const*bool是标准转换,但是std::string是用户定义的",通常被认为是更差").您的代码应该value设置为其std::string替代项,但是您的库的std::variant实现已损坏.可能已经打开了一个问题凭单,但是如果没有,则是要打开一个问题凭单的理由.如果您在使用库时遇到困难,则可以将文字显式标记为std::string应该有效:

That last clause is there for this very situation. Because lots of things can convert to bool, but we don't usually intend this conversion, that clause causes conversion sequences that would not normally be selected to be selected (char const* to bool is a standard conversion, but to std::string is "user-defined", which is normally considered "worse"). Your code should set value to its std::string alternative, but your library's implementation of std::variant is broken. There's probably an issue ticket already opened, but if there isn't, this is grounds to open one. If you're stuck with your library, explicitly marking the literal as a std::string should work:

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


对于优雅的问题,请使用缩写模板lambda.


For the elegance question, use an abbreviated template lambda.

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,因为它不需要访问private成员.

Also, your friend declaration doesn't match the definition. Actually, it shouldn't be friended anyway, since it needs no access to private members.

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

这篇关于呼叫&lt;&lt;在std :: variant中持有的类型上的运算符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆