无法使用std :: variant重载运算符<<()传输std :: endl [英] Can't stream std::endl with overloaded operator<<() for std::variant
问题描述
此答案描述了如何流式传输独立的std::variant
.但是,当std::variant
存储在std::unordered_map
中时,它似乎不起作用.
This answer describes how to stream a standalone std::variant
. However, it doesn't seem to work when std::variant
is stored in a std::unordered_map
.
以下示例:
#include <iostream>
#include <string>
#include <variant>
#include <complex>
#include <unordered_map>
// https://stackoverflow.com/a/46893057/8414561
template<typename... Ts>
std::ostream& operator<<(std::ostream& os, const std::variant<Ts...>& v)
{
std::visit([&os](auto&& arg) {
os << arg;
}, v);
return os;
}
int main()
{
using namespace std::complex_literals;
std::unordered_map<int, std::variant<int, std::string, double, std::complex<double>>> map{
{0, 4},
{1, "hello"},
{2, 3.14},
{3, 2. + 3i}
};
for (const auto& [key, value] : map)
std::cout << key << "=" << value << std::endl;
}
无法编译:
In file included from main.cpp:3:
/usr/local/include/c++/8.1.0/variant: In instantiation of 'constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor':
/usr/local/include/c++/8.1.0/variant:1038:11: required from 'class std::variant<>'
main.cpp:27:50: required from here
/usr/local/include/c++/8.1.0/variant:300:4: error: invalid use of incomplete type 'struct std::__detail::__variant::_Nth_type<0>'
is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/c++/8.1.0/variant:58:12: note: declaration of 'struct std::__detail::__variant::_Nth_type<0>'
struct _Nth_type;
^~~~~~~~~
/usr/local/include/c++/8.1.0/variant: In instantiation of 'class std::variant<>':
main.cpp:27:50: required from here
/usr/local/include/c++/8.1.0/variant:1051:39: error: static assertion failed: variant must have at least one alternative
static_assert(sizeof...(_Types) > 0,
~~~~~~~~~~~~~~~~~~^~~
为什么会发生?如何解决?
Why does it happen? How is it possible to fix it?
推荐答案
在 [temp.arg.explicit]/3 ,我们有一个惊人的句子:
In [temp.arg.explicit]/3, we have this amazing sentence:
否则未推断出的尾随模板参数包将被推断为空的模板参数序列.
A trailing template parameter pack not otherwise deduced will be deduced to an empty sequence of template arguments.
这是什么意思?什么是尾随模板参数包?不以其他方式推论是什么意思?这些都是很好的问题,实际上并没有答案.但这会产生非常有趣的结果.考虑:
What does this mean? What is a trailing template parameter pack? What does not otherwise deduced mean? These are all good questions that don't really have answers. But this has very interesting consequences. Consider:
template <typename... Ts> void f(std::tuple<Ts...>);
f({}); // ok??
这是...格式正确的.我们无法推断Ts...
,因此我们将其推断为空.剩下的是std::tuple<>
,这是一个完全有效的类型-甚至可以使用{}
实例化的一个完全有效的类型.这样就可以编译了!
This is... well-formed. We can't deduce Ts...
so we deduce it as empty. That leaves us with std::tuple<>
, which is a perfectly valid type - and a perfectly valid type that can even be instantiated with {}
. So this compiles!
那么,当我们从空参数包中推断出的东西不是有效类型时,会发生什么呢?这是示例:
So what happens when the thing we deduce from the empty parameter pack we conjured up isn't a valid type? Here's an example:
template <class... Ts>
struct Y
{
static_assert(sizeof...(Ts)>0, "!");
};
template <class... Ts>
std::ostream& operator<<(std::ostream& os, Y<Ts...> const& )
{
return os << std::endl;
}
operator<<
是一个潜在的候选者,但是推论失败了……或者看起来如此.直到我们将Ts...
变空.但是Y<>
是无效的类型!我们甚至没有试图找出无法从std::endl
构造Y<>
的原因-我们已经已经失败了.
The operator<<
is a potential candidate, but deduction fails... or so it would seem. Until we conjure up Ts...
as empty. But Y<>
is an invalid type! We don't even try to find out that we can't construct a Y<>
from std::endl
- we have already failed.
与variant
的情况基本上相同,因为variant<>
不是有效的类型.
This is fundamentally the same situation you have with variant
, because variant<>
is not a valid type.
简单的解决方法是简单地将功能模板从variant<Ts...>
更改为variant<T, Ts...>
.这不再可以归结为variant<>
,这甚至不可能,因此我们没有问题.
The easy fix is to simply change your function template from taking a variant<Ts...>
to a variant<T, Ts...>
. This can no longer deduce to variant<>
, which isn't even a possible thing, so we don't have a problem.
这篇关于无法使用std :: variant重载运算符<<()传输std :: endl的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!