呼叫<<在std :: variant中持有的类型上的运算符? [英] Calling << operator on types held in a 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 expressionF(std::forward<T>(t))
if there was an overload of imaginary functionF(T_i)
for everyT_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 declarationT_i x[] = { std::forward<T>(t) };
is valid for some invented variablex
;
如果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 friend
ed 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 ^^^^^
这篇关于呼叫<<在std :: variant中持有的类型上的运算符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!