【问题标题】:Is it possible to define operator<< for templated types?是否可以为模板类型定义 operator<< ?
【发布时间】:2021-07-25 19:59:16
【问题描述】:

我想打印std::maps等STL类型的值,例如:

int main()
{
    std::map<int, std::vector<int>> t = {{1, {2, 3}}, {4, {5}}};
    std::cout << t << std::endl;
    return 0;
}

这会失败,因为没有为这些类型定义 operator&lt;&lt;

如果我尝试自己定义它,我还必须转发声明我的定义,否则编译失败:

error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
        o << sep << "{" << x.first << ", " << x.second << "}";
...
note: 'operator<<' should be declared prior to the call site std::ostream& operator<< (std::ostream& o, const std::vector<T>& vec)

这很不幸,因为必须前向声明方法需要为每种类型使用两个包含(先前向声明,然后是实现)。

  • 有没有办法以模块化的方式为模板类型定义operator&lt;&lt;
  • 这就是为什么operator&lt;&lt; 不能用于开箱即用的 STL 类型的原因吗?

参考:A working implementation with forward declarations

【问题讨论】:

  • 请在问题中包含minimal reproducible example
  • @largest_prime_is_463035818 不就是我在最后一行中链接到的参考吗?
  • 如果您发布有错误的代码,这会更简单。当我必须修改重现错误的代码时,我可能会以不同于产生错误的代码的方式对其进行更改。尽管当我删除前向声明时代码实际上也可以正常编译:godbolt.org/z/GxThvGd1M.
  • 不允许将定义放入命名空间std(极少数例外)。一种解决方法是像您一样进行前向声明,或者使用自定义类型,如 struct MyInt{ int x; }; 而不是 int。这些自定义类型具有关联的命名空间,并且所有模板参数的命名空间都与 ADL 相关
  • @largest_prime_is_463035818:原因之一是 ODR(定义 operator &lt;&lt;(std::ostream&amp;, std::map&lt;K, V&gt;) 的几个库)。

标签: c++ templates stl


【解决方案1】:

在您的要点中,std::map&lt;K, V&gt;operator&lt;&lt; 重载是在 std::vector&lt;T&gt; 的重载之前定义的。

GCC 很乐意接受您的代码而不会引发任何错误,而 Clang(我猜是您正在使用的那个)完全拒绝使用您发布的消息编译它,这基本上说 operator&lt;&lt; 对 @ 没有好处987654325@尚未定义。

我应该仔细检查标准在这种情况下的规定,但我的第一印象是 GCC 接受您的代码,因为 operator&lt;&lt;(ostream&amp;,const std::vector&lt;T&gt;&amp;) 是在实例化时定义的(这发生在 main(),当您调用 std::cout &lt;&lt; t 时) ,而 Clang 严格认为只有在定义模板时已知的那些函数是可见的。

如果您为 operator&lt;&lt;(ostream&amp;,const std::vector&lt;T&gt;&amp;) 添加前向声明,它会在 operator&lt;&lt;(ostream&amp;, const std::map&lt;K,V&gt;) 中明确可见,并且 Clang 会编译您的代码。

只需交换函数也足以让您的特定代码在 Clang 下构建,但如果您尝试这样做,它将再次中断

int main()
{
    std::vector<std::map<int, int>> u = {{{1, 2},{3, 4}}, {{4, 5}}};

    std::cout << u << std::endl;

    return 0;
}

因为现在 Clang 不会在 std::vector&lt;T&gt; 的实现中看到正确的 std::map&lt;K, V&gt; 重载(尽管使用 GCC,它仍然可以正常构建)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 1970-01-01
    相关资源
    最近更新 更多