我有下面的代码,其中我定义了一个ostream <<-operator重载变量模板参数类型VariantContainer,它包含一个std::variant并使用来自VariantContainer for std::variant的可变模板参数
#include <iostream>
#include <variant>
template<typename... VARIANT_TYPES>
struct VariantContainer {
std::variant<VARIANT_TYPES...> var;
};
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst) {
std::visit([&os](auto&& elem){os<<elem;}, inst.var);
return os;
}
int main() {
//If the following line is commented out, a compiler error is raised
//std::cout << std::endl;
}此示例编译在gcc-8、gcc-9和with 10上,其中--std=c++17 (https://godbolt.org/z/5qvT3f).但是,当我启用std::cout-line时,会得到以下编译器错误:
In file included from <source>:2:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1279:11: required from 'class std::variant<>'
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:286:4: error: invalid use of incomplete type 'struct std::__detail::__variant::_Nth_type<0>'
286 | is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:61:12: note: declaration of 'struct std::__detail::__variant::_Nth_type<0>'
61 | struct _Nth_type;
| ^~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'class std::variant<>':
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1298:39: error: static assertion failed: variant must have at least one alternative
1298 | static_assert(sizeof...(_Types) > 0,
| ~~~~~~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/move.h:57,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/nested_exception.h:40,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/exception:148,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ios:39,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:38,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1361:2: required from 'class std::variant<>'
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
2554 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
ASM generation compiler returned: 1
In file included from <source>:2:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1279:11: required from 'class std::variant<>'
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:286:4: error: invalid use of incomplete type 'struct std::__detail::__variant::_Nth_type<0>'
286 | is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:61:12: note: declaration of 'struct std::__detail::__variant::_Nth_type<0>'
61 | struct _Nth_type;
| ^~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'class std::variant<>':
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1298:39: error: static assertion failed: variant must have at least one alternative
1298 | static_assert(sizeof...(_Types) > 0,
| ~~~~~~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/move.h:57,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/nested_exception.h:40,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/exception:148,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ios:39,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:38,
from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1361:2: required from 'class std::variant<>'
<source>:6:36: required from 'struct VariantContainer<>'
<source>:17:23: required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
2554 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
Execution build compiler returned: 1在我看来,当编译器看到std::endl时,它试图用零模板参数实例化VariantContainer (这是不允许的,因为std::variant static_assert就是这样)。但是为什么编译器要这样做,我如何防止这种情况呢?
当然,我很清楚,我可以在to_string中创建一个VariantContainer方法来防止这种情况,或者提供一个更通用的std::ostream <<-operator版本(比如template<typename T> std::ostream& operator<<(std::ostream &, const T& ) {...},并通过std::enable_if对其进行约束)。但我真的不喜欢这样。
发布于 2020-12-29 13:01:17
std::endl是一个函数模板,重载也是如此。
使用
std::cout << std::endl;
我们必须采取(正确) 重载函数的地址。
为此,我们必须看到每个operator<<之间是否存在一些约束签名的因素。
确实有来自basic_stream的,特别是
basic_ostream& operator<<(
std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );(这是预期的最佳匹配)。
但我们还必须检查其他问题,特别是有问题的:
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);而且,由于VARIANT_TYPES不能从函数集中还原,所以VARIANT_TYPES是空包。
现在检查VariantContainer<>(std::endl)是否会强制函数签名,因此实例化VariantContainer<>构造函数,因为std::variant<>导致了硬错误。
现在,可能的解决方案应该避免硬错误
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);VariantContainer<>提供(有效或甚至不完全)专门化template <typename T, typename... Ts>
class VariantContainer;(不需要更改operator <<,因为SFINAE将适用于无效的VariantContainer<>)
operator<<更改为template <typename T, typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<T, VARIANT_TYPES...>& inst);发布于 2020-12-29 12:17:43
同时,我尝试像这样定义VariantContainer:
template<typename VARIANT_TYPE, typename... VARIANT_TYPES>
struct VariantContainer {
std::variant<VARIANT_TYPE, VARIANT_TYPES...> var;
};只是为了确保必须给出一个模板参数。
现在起作用了!
#include <iostream>
#include <variant>
struct A{
int a;
};
struct B{
int b;
};
std::ostream& operator<<(std::ostream& os, const A& inst) {os << inst.a; return os;}
std::ostream& operator<<(std::ostream& os, const B& inst) {os << inst.b; return os;}
template<typename VARIANT_TYPE, typename... VARIANT_TYPES>
struct VariantContainer {
std::variant<VARIANT_TYPE, VARIANT_TYPES...> var;
};
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst) {
std::visit([&os](auto&& elem){os<<elem;}, inst.var);
return os;
}
int main() {
VariantContainer<A, B> insta{A{1}};
VariantContainer<A, B> instb{B{1}};
std::cout << "If I want to print something, I get a compiler error." << insta << " " << instb<< std::endl;
}https://stackoverflow.com/questions/65491869
复制相似问题