重载运算符< < 不能与GCC一起编译

u5rb5r59  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(153)

为什么下面的代码是由MSVC编译的,而不是GCC?

#include <iostream>
#include <sstream>

template <class C>
class basic_format
{
public:
    
    template <typename T>
    basic_format & operator << (const T & val)
    {
        out << val;
        return *this;
    }

    std::basic_string<C> str() const { return out.str(); }

    operator std::basic_string<C>() const { return str(); }

private:
    
    std::basic_ostringstream<C> out;
};

using aformat = basic_format<char>;
using wformat = basic_format<wchar_t>;

namespace other
{
    class A {};
}

template <class C>
std::basic_ostream<C>& operator << (std::basic_ostream<C>& out, const other::A&)
{
    return out << static_cast<C>('A');
}

int main()
{
    other::A a;
    
    const std::string val = aformat() << a;

    std::cout << val << std::endl;
    
    return 0;
}

GCC 12错误:

g++ -std=c++20 -pthread a.cpp -o a

  a.cpp: In instantiation of ‘basic_format<C>& basic_format<C>::operator<<(const T&) [with T = other::A; C = char]’:
  a.cpp:43:42:   required from here
  a.cpp:12:13: error: no match for ‘operator<<’ (operand types are ‘std::__cxx11::basic_ostringstream<char>’ and ‘const other::A’)
  12 |         out << val;
        |         ~~~~^~~~~~
  In file included from /usr/include/c++/12/iostream:39,
              from a.cpp:1:
  /usr/include/c++/12/ostream:108:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(__ostream_type& (*)(__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  108 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:108:36: note:   no known conversion for argument 1 from ‘const other::A’ to ‘std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&)’ {aka ‘std::basic_ostream<char>& (*)(std::basic_ostream<char>&)’}
  108 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
        |                  ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
  /usr/include/c++/12/ostream:117:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(__ios_type& (*)(__ios_type&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>; __ios_type = std::basic_ios<char>]’
  117 |       operator<<(__ios_type& (*__pf)(__ios_type&))
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:117:32: note:   no known conversion for argument 1 from ‘const other::A’ to ‘std::basic_ostream<char>::__ios_type& (*)(std::basic_ostream<char>::__ios_type&)’ {aka ‘std::basic_ios<char>& (*)(std::basic_ios<char>&)’}
  117 |       operator<<(__ios_type& (*__pf)(__ios_type&))
        |                  ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
  /usr/include/c++/12/ostream:127:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  127 |       operator<<(ios_base& (*__pf) (ios_base&))
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:127:30: note:   no known conversion for argument 1 from ‘const other::A’ to ‘std::ios_base& (*)(std::ios_base&)’
  127 |       operator<<(ios_base& (*__pf) (ios_base&))
        |                  ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
  /usr/include/c++/12/ostream:166:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  166 |       operator<<(long __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:166:23: note:   no known conversion for argument 1 from ‘const other::A’ to ‘long int’
  166 |       operator<<(long __n)
        |                  ~~~~~^~~
  /usr/include/c++/12/ostream:170:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  170 |       operator<<(unsigned long __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:170:32: note:   no known conversion for argument 1 from ‘const other::A’ to ‘long unsigned int’
  170 |       operator<<(unsigned long __n)
        |                  ~~~~~~~~~~~~~~^~~
  /usr/include/c++/12/ostream:174:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  174 |       operator<<(bool __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:174:23: note:   no known conversion for argument 1 from ‘const other::A’ to ‘bool’
  174 |       operator<<(bool __n)
        |                  ~~~~~^~~
  In file included from /usr/include/c++/12/ostream:833:
  /usr/include/c++/12/bits/ostream.tcc:91:5: note: candidate: ‘std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char; _Traits = std::char_traits<char>]’
  91 |     basic_ostream<_CharT, _Traits>::
        |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  /usr/include/c++/12/bits/ostream.tcc:92:22: note:   no known conversion for argument 1 from ‘const other::A’ to ‘short int’
  92 |     operator<<(short __n)
        |                ~~~~~~^~~
  /usr/include/c++/12/ostream:181:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  181 |       operator<<(unsigned short __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:181:33: note:   no known conversion for argument 1 from ‘const other::A’ to ‘short unsigned int’
  181 |       operator<<(unsigned short __n)
        |                  ~~~~~~~~~~~~~~~^~~
  /usr/include/c++/12/bits/ostream.tcc:105:5: note: candidate: ‘std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char; _Traits = std::char_traits<char>]’
  105 |     basic_ostream<_CharT, _Traits>::
        |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  /usr/include/c++/12/bits/ostream.tcc:106:20: note:   no known conversion for argument 1 from ‘const other::A’ to ‘int’
  106 |     operator<<(int __n)
        |                ~~~~^~~
  /usr/include/c++/12/ostream:192:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  192 |       operator<<(unsigned int __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:192:31: note:   no known conversion for argument 1 from ‘const other::A’ to ‘unsigned int’
  192 |       operator<<(unsigned int __n)
        |                  ~~~~~~~~~~~~~^~~
  /usr/include/c++/12/ostream:201:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  201 |       operator<<(long long __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:201:28: note:   no known conversion for argument 1 from ‘const other::A’ to ‘long long int’
  201 |       operator<<(long long __n)
        |                  ~~~~~~~~~~^~~
  /usr/include/c++/12/ostream:205:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  205 |       operator<<(unsigned long long __n)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:205:37: note:   no known conversion for argument 1 from ‘const other::A’ to ‘long long unsigned int’
  205 |       operator<<(unsigned long long __n)
        |                  ~~~~~~~~~~~~~~~~~~~^~~
  /usr/include/c++/12/ostream:220:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  220 |       operator<<(double __f)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:220:25: note:   no known conversion for argument 1 from ‘const other::A’ to ‘double’
  220 |       operator<<(double __f)
        |                  ~~~~~~~^~~
  /usr/include/c++/12/ostream:224:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  224 |       operator<<(float __f)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:224:24: note:   no known conversion for argument 1 from ‘const other::A’ to ‘float’
  224 |       operator<<(float __f)
        |                  ~~~~~~^~~
  /usr/include/c++/12/ostream:232:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]’
  232 |       operator<<(long double __f)
        |       ^~~~~~~~
  /usr/include/c++/12/ostream:232:30: note:   no known conversion for argument 1 from ‘const other::A’ to ‘long double’

但是如果我在命名空间之外(在全局级别)声明class A,它就会开始被GCC编译。

ajsxfq5m

ajsxfq5m1#

GCC是正确的。默认情况下,MSVC在模板定义中有不符合标准的查找规则(我假设可以通过/permissive-或将语言设置为C++20或更高版本来禁用,这显然是不正确的)。
一个只在模板定义后 * 声明的函数,只能在模板参数推导中通过从模板示例化的Angular 进行依赖于参数的查找来考虑,而不是通过从示例化的Angular 进行通常的非限定查找。
所以对于out << val

template <typename T>
basic_format & operator << (const T & val)
{
    out << val;
    return *this;
}

全局命名空间范围内的operator<<重载只能通过ADL来考虑。
ADL只考虑与参数/操作数(或其他关联类型)所在的名称空间范围相同的声明。
这就是为什么非成员运算符重载总是与它所作用的类(模板)属于同一个命名空间范围的原因。

相关问题