首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在ostream‘<<’中使用std::endl --带有包含变量成员的可变模板类的操作符重载会导致编译器错误

在ostream‘<<’中使用std::endl --带有包含变量成员的可变模板类的操作符重载会导致编译器错误
EN

Stack Overflow用户
提问于 2020-12-29 12:07:29
回答 2查看 112关注 0票数 2

我有下面的代码,其中我定义了一个ostream <<-operator重载变量模板参数类型VariantContainer,它包含一个std::variant并使用来自VariantContainer for std::variant的可变模板参数

代码语言:javascript
复制
#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时,会得到以下编译器错误:

代码语言:javascript
复制
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对其进行约束)。但我真的不喜欢这样。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-29 13:01:17

std::endl是一个函数模板,重载也是如此。

使用

std::cout << std::endl;

我们必须采取(正确) 重载函数的地址

为此,我们必须看到每个operator<<之间是否存在一些约束签名的因素。

确实有来自basic_stream的,特别是

代码语言:javascript
复制
basic_ostream& operator<<(
    std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );

(这是预期的最佳匹配)。

但我们还必须检查其他问题,特别是有问题的:

代码语言:javascript
复制
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<>导致了硬错误。

现在,可能的解决方案应该避免硬错误

代码语言:javascript
复制
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);
  • 为类VariantContainer<>提供(有效或甚至不完全)专门化
  • 将主模板更改为
代码语言:javascript
复制
template <typename T, typename... Ts> 
class VariantContainer;

(不需要更改operator <<,因为SFINAE将适用于无效的VariantContainer<>)

  • operator<<更改为
代码语言:javascript
复制
template <typename T, typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<T, VARIANT_TYPES...>& inst);
票数 3
EN

Stack Overflow用户

发布于 2020-12-29 12:17:43

同时,我尝试像这样定义VariantContainer:

代码语言:javascript
复制
template<typename VARIANT_TYPE, typename... VARIANT_TYPES>
struct VariantContainer {
    std::variant<VARIANT_TYPE, VARIANT_TYPES...> var;
};

只是为了确保必须给出一个模板参数。

现在起作用了!

代码语言:javascript
复制
#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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65491869

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档