对 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);
};
我正在尝试像这样实现 << 运算符:
// 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!忽略采用 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)
's cppreference page,它
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:
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
;
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
是 "user-defined",通常被认为是 "worse")。您的代码 应该 将 value
设置为其 std::string
替代项,但是您的库对 std::variant
的实现已损坏。可能已经打开了一张问题单,但如果没有,则可以打开一张。如果你坚持使用你的库,明确地将文字标记为 std::string
应该可行:
literal.value = std::string("Hello World");
对于优雅问题,使用缩写模板 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
成员。
// declaration in header, outside of any class, as a free function
std::ostream &operator<<(std::ostream&, Literal const&);
// was missing const ^^^^^
我有这样的结构:
// Literal.hpp
struct Literal
{
std::variant<
std::nullptr_t,
std::string,
double,
bool
>
value;
friend std::ostream &operator<<(std::ostream &os, Literal &literal);
};
我正在尝试像这样实现 << 运算符:
// 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!忽略采用 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)
's cppreference page,它
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:
An overload
F(T_i)
is only considered if the declarationT_i x[] = { std::forward<T>(t) };
is valid for some invented variablex
;If
T_i
is (possibly cv-qualified)bool
,F(T_i)
is only considered ifstd:remove_cvref_t<T>
is alsobool
.
最后一个条款就是针对这种情况。因为很多东西都可以转换为 bool
,但我们通常不打算进行这种转换,所以该子句会导致选择通常不会被选择的转换序列(char const*
到 bool
是标准转换,但到 std::string
是 "user-defined",通常被认为是 "worse")。您的代码 应该 将 value
设置为其 std::string
替代项,但是您的库对 std::variant
的实现已损坏。可能已经打开了一张问题单,但如果没有,则可以打开一张。如果你坚持使用你的库,明确地将文字标记为 std::string
应该可行:
literal.value = std::string("Hello World");
对于优雅问题,使用缩写模板 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
成员。
// declaration in header, outside of any class, as a free function
std::ostream &operator<<(std::ostream&, Literal const&);
// was missing const ^^^^^