最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

templates - operator<< overload for std::errc is not detected in a std::formatter specialization for custom class

programmeradmin4浏览0评论

I have a template class and a custom std::formatter specialization for that class to be able to format and print it. When I create this class with std::errc data and try to print it, the compiler complains:

error: no match for 'operator<<' (operand types are 'std::ostringstream' {aka 'std::__cxx11::basic_ostringstream<char>'} and 'const std::errc')
 59 | oss << my_class.data();

The compile error is because it cannot detect the operator<< overload that I created for std::errc. When I try to print a std::errc instance directly in main(), it can find and run the operator<< overload that I created and prints the correct data.

#include <format>
#include <iostream>
#include <sstream>
#include <string>
#include <system_error>

template <typename DATA_T>
class MyClass {
   public:
    MyClass(std::string name, DATA_T data, std::string location)
        : name_(std::move(name)),
          data_(std::move(data)),
          location_(std::move(location)) {}

    const std::string& name() const { return name_; }
    const DATA_T& data() const { return data_; }
    const std::string& location() const { return location_; }

   private:
    std::string name_;
    DATA_T data_;
    std::string location_;
};

// Specialization for std::errc for std::format
template <>
struct std::formatter<std::errc> : std::formatter<std::string> {
    static auto format(const std::errc& err, std::format_context& ctx) {
        auto error_code = std::make_error_code(err);
        return std::format_to(ctx.out(), "Error {}: {}", error_code.value(),
                              error_code.message());
    }
};

// Overload operator<< for std::errc
inline std::ostream& operator<<(std::ostream& ostr, const std::errc& err) {
    return ostr << std::format("{}", err);
}

// Custom concept to check if a type is streamable via operator<<
template <typename T>
concept OStreamable = requires(std::ostream& os, const T& t) {
    { os << t } -> std::convertible_to<std::ostream&>;
};

// Specialization for MyClass for std::format
template <typename DATA_T>
struct std::formatter<MyClass<DATA_T>> : std::formatter<std::string> {
    auto format(const MyClass<DATA_T>& my_class,
                std::format_context& ctx) const {
        std::string result = std::format("Name: {}\n", my_class.name());

        // Check if DATA_T is streamable
        if constexpr (OStreamable<DATA_T>) {
            std::ostringstream oss;
            oss << my_class.data();
            result += "Data: " + oss.str();
        }

        result += std::format("\nLocation: {}", my_class.location());
        return std::format_to(ctx.out(), "{}", result);
    }
};

// Overload operator<< for MyClass
template <typename T>
inline std::ostream& operator<<(std::ostream& ostr,
                                const MyClass<T>& my_class) {
    return ostr << std::format("{}", my_class);
}

int main() {
    MyClass<std::errc> my_class{"Errc Object", std::errc::invalid_argument,
                                "Main Frame"};
    std::cout << std::errc::invalid_argument << '\n';  // Works fine
    // std::println("{}", my_class); // Creates a compile error.
}

If I add using ::operator<< statement before oss << my_class.data(), the compile error is gone.

How is it possible that the operator<< overload can be found when it is called in main() but cannot be found in my std::formatter specialization? Is there a way to fix it without adding a using ::operator<< statement?

The complete compiler error:

<source>:60:17: error: invalid operands to binary expression ('std::ostringstream' (aka 'basic_ostringstream<char>') and 'const std::errc')
   60 |             oss << my_class.data();
      |             ~~~ ^  ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2529:14: note: in instantiation of member function 'std::formatter<MyClass<std::errc>>::format' requested here
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |              ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2529:9: note: in instantiation of requirement here
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |         ^~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2527:7: note: while substituting template arguments into constraint expression here
 2527 |           && requires (const _Formatter __cf, _Tp&& __t, _Context __fc)
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2528 |     {
      |     ~
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2530 |     };
      |     ~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3475:11: note: while checking the satisfaction of concept '__formattable_with<MyClass<std::errc>, std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::formatter<MyClass<std::errc>>, std::basic_format_parse_context<char>>' requested here
 3475 |         requires __format::__formattable_with<_Tp, _Context>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3475:11: note: while substituting template arguments into constraint expression here
 3475 |         requires __format::__formattable_with<_Tp, _Context>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3743:31: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
 3743 |           basic_format_arg<_Context> __arg(__v);
      |                                      ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3754:12: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_S_make_elt<MyClass<std::errc>>' requested here
 3754 |         : _M_args{_S_make_elt(__a)...}
      |                   ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3804:14: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_Arg_store<MyClass<std::errc>>' requested here
 3804 |       return _Store(__fmt_args...);
      |              ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/print:123:9: note: in instantiation of function template specialization 'std::format<MyClass<std::errc> &>' requested here
  123 |                  std::format(__fmt, std::forward<_Args>(__args)...));
      |                       ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/print:129:12: note: in instantiation of function template specialization 'std::println<MyClass<std::errc> &>' requested here
  129 |     { std::println(stdout, __fmt, std::forward<_Args>(__args)...); }
      |            ^
<source>:78:10: note: in instantiation of function template specialization 'std::println<MyClass<std::errc> &>' requested here
   78 |     std::println("{}", my_class); // Creates a compile error.
      |          ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/system_error:339:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const error_code' for 2nd argument
  339 |     operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)
      |     ^                                                ~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:570:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char' for 2nd argument
  570 |     operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
      |     ^                                                 ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:576:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char' for 2nd argument
  576 |     operator<<(basic_ostream<char, _Traits>& __out, char __c)
      |     ^                                               ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:587:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'signed char' for 2nd argument
  587 |     operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
      |     ^                                               ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:592:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'unsigned char' for 2nd argument
  592 |     operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
      |     ^                                               ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:601:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'wchar_t' for 2nd argument
  601 |     operator<<(basic_ostream<char, _Traits>&, wchar_t) = delete;
      |     ^                                         ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:606:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char8_t' for 2nd argument
  606 |     operator<<(basic_ostream<char, _Traits>&, char8_t) = delete;
      |     ^                                         ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:611:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char16_t' for 2nd argument
  611 |     operator<<(basic_ostream<char, _Traits>&, char16_t) = delete;
      |     ^                                         ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:615:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char32_t' for 2nd argument
  615 |     operator<<(basic_ostream<char, _Traits>&, char32_t) = delete;
      |     ^                                         ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:668:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char *' for 2nd argument
  668 |     operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:681:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const signed char *' for 2nd argument
  681 |     operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:686:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const unsigned char *' for 2nd argument
  686 |     operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:695:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const wchar_t *' for 2nd argument
  695 |     operator<<(basic_ostream<char, _Traits>&, const wchar_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:700:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char8_t *' for 2nd argument
  700 |     operator<<(basic_ostream<char, _Traits>&, const char8_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:705:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char16_t *' for 2nd argument
  705 |     operator<<(basic_ostream<char, _Traits>&, const char16_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:709:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char32_t *' for 2nd argument
  709 |     operator<<(basic_ostream<char, _Traits>&, const char32_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/bits/ostream.tcc:307:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char *' for 2nd argument
  307 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)
      |     ^                                                 ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/cstddef:125:5: note: candidate function template not viable: no known conversion from 'std::ostringstream' (aka 'basic_ostringstream<char>') to 'byte' for 1st argument
  125 |     operator<<(byte __b, _IntegerType __shift) noexcept
      |     ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:560:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. 'std::errc')
  560 |     operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/string_view:760:5: note: candidate template ignored: could not match 'basic_string_view<_CharT, _Traits>' against 'std::errc'
  760 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/bits/basic_string.h:4077:5: note: candidate template ignored: could not match 'const basic_string<_CharT, _Traits, _Alloc>' against 'const std::errc'
 4077 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:621:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  621 |     operator<<(basic_ostream<wchar_t, _Traits>&, char8_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:626:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  626 |     operator<<(basic_ostream<wchar_t, _Traits>&, char16_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:630:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  630 |     operator<<(basic_ostream<wchar_t, _Traits>&, char32_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:651:5: note: candidate template ignored: could not match 'const _CharT *' against 'std::errc'
  651 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:715:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  715 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char8_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:720:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  720 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char16_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:724:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  724 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char32_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:807:5: note: candidate template ignored: substitution failure [with _Ostream = std::ostringstream &, _Tp = std::errc]: constraints not satisfied for alias template '__rvalue_stream_insertion_t' [with _Os = std::basic_ostringstream<char> &, _Tp = std::errc]
  806 |     inline __rvalue_stream_insertion_t<_Ostream, _Tp>
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  807 |     operator<<(_Ostream&& __os, const _Tp& __x)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:116:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__ostream_type &(*)(__ostream_type &)' (aka 'basic_ostream<char, std::char_traits<char>> &(*)(basic_ostream<char, std::char_traits<char>> &)') for 1st argument
  116 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:125:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__ios_type &(*)(__ios_type &)' (aka 'basic_ios<char, std::char_traits<char>> &(*)(basic_ios<char, std::char_traits<char>> &)') for 1st argument
  125 |       operator<<(__ios_type& (*__pf)(__ios_type&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:135:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'ios_base &(*)(ios_base &)' for 1st argument
  135 |       operator<<(ios_base& (*__pf) (ios_base&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:174:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long' for 1st argument
  174 |       operator<<(long __n)
      |       ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:178:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned long' for 1st argument
  178 |       operator<<(unsigned long __n)
      |       ^          ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:182:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'bool' for 1st argument
  182 |       operator<<(bool __n)
      |       ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:186:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'short' for 1st argument
  186 |       operator<<(short __n);
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:189:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned short' for 1st argument
  189 |       operator<<(unsigned short __n)
      |       ^          ~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:197:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'int' for 1st argument
  197 |       operator<<(int __n);
      |       ^          ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:200:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned int' for 1st argument
  200 |       operator<<(unsigned int __n)
      |       ^          ~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:209:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long long' for 1st argument
  209 |       operator<<(long long __n)
      |       ^          ~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:213:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned long long' for 1st argument
  213 |       operator<<(unsigned long long __n)
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:228:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'double' for 1st argument
  228 |       operator<<(double __f)
      |       ^          ~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:232:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'float' for 1st argument
  232 |       operator<<(float __f)
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:240:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long double' for 1st argument
  240 |       operator<<(long double __f)
      |       ^          ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:298:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'const void *' for 1st argument; take the address of the argument with &
  298 |       operator<<(const void* __p)
      |       ^          ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:303:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'nullptr_t' (aka 'std::nullptr_t') for 1st argument
  303 |       operator<<(nullptr_t)
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:310:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'const volatile void *' for 1st argument; take the address of the argument with &
  310 |       operator<<(const volatile void* __p)
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:336:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__streambuf_type *' (aka 'basic_streambuf<char, std::char_traits<char>> *') for 1st argument
  336 |       operator<<(__streambuf_type* __sb);
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Compiler returned: 1

The full code and compile error can be seen at:

I have a template class and a custom std::formatter specialization for that class to be able to format and print it. When I create this class with std::errc data and try to print it, the compiler complains:

error: no match for 'operator<<' (operand types are 'std::ostringstream' {aka 'std::__cxx11::basic_ostringstream<char>'} and 'const std::errc')
 59 | oss << my_class.data();

The compile error is because it cannot detect the operator<< overload that I created for std::errc. When I try to print a std::errc instance directly in main(), it can find and run the operator<< overload that I created and prints the correct data.

#include <format>
#include <iostream>
#include <sstream>
#include <string>
#include <system_error>

template <typename DATA_T>
class MyClass {
   public:
    MyClass(std::string name, DATA_T data, std::string location)
        : name_(std::move(name)),
          data_(std::move(data)),
          location_(std::move(location)) {}

    const std::string& name() const { return name_; }
    const DATA_T& data() const { return data_; }
    const std::string& location() const { return location_; }

   private:
    std::string name_;
    DATA_T data_;
    std::string location_;
};

// Specialization for std::errc for std::format
template <>
struct std::formatter<std::errc> : std::formatter<std::string> {
    static auto format(const std::errc& err, std::format_context& ctx) {
        auto error_code = std::make_error_code(err);
        return std::format_to(ctx.out(), "Error {}: {}", error_code.value(),
                              error_code.message());
    }
};

// Overload operator<< for std::errc
inline std::ostream& operator<<(std::ostream& ostr, const std::errc& err) {
    return ostr << std::format("{}", err);
}

// Custom concept to check if a type is streamable via operator<<
template <typename T>
concept OStreamable = requires(std::ostream& os, const T& t) {
    { os << t } -> std::convertible_to<std::ostream&>;
};

// Specialization for MyClass for std::format
template <typename DATA_T>
struct std::formatter<MyClass<DATA_T>> : std::formatter<std::string> {
    auto format(const MyClass<DATA_T>& my_class,
                std::format_context& ctx) const {
        std::string result = std::format("Name: {}\n", my_class.name());

        // Check if DATA_T is streamable
        if constexpr (OStreamable<DATA_T>) {
            std::ostringstream oss;
            oss << my_class.data();
            result += "Data: " + oss.str();
        }

        result += std::format("\nLocation: {}", my_class.location());
        return std::format_to(ctx.out(), "{}", result);
    }
};

// Overload operator<< for MyClass
template <typename T>
inline std::ostream& operator<<(std::ostream& ostr,
                                const MyClass<T>& my_class) {
    return ostr << std::format("{}", my_class);
}

int main() {
    MyClass<std::errc> my_class{"Errc Object", std::errc::invalid_argument,
                                "Main Frame"};
    std::cout << std::errc::invalid_argument << '\n';  // Works fine
    // std::println("{}", my_class); // Creates a compile error.
}

If I add using ::operator<< statement before oss << my_class.data(), the compile error is gone.

How is it possible that the operator<< overload can be found when it is called in main() but cannot be found in my std::formatter specialization? Is there a way to fix it without adding a using ::operator<< statement?

The complete compiler error:

<source>:60:17: error: invalid operands to binary expression ('std::ostringstream' (aka 'basic_ostringstream<char>') and 'const std::errc')
   60 |             oss << my_class.data();
      |             ~~~ ^  ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2529:14: note: in instantiation of member function 'std::formatter<MyClass<std::errc>>::format' requested here
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |              ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2529:9: note: in instantiation of requirement here
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |         ^~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2527:7: note: while substituting template arguments into constraint expression here
 2527 |           && requires (const _Formatter __cf, _Tp&& __t, _Context __fc)
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2528 |     {
      |     ~
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2530 |     };
      |     ~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3475:11: note: while checking the satisfaction of concept '__formattable_with<MyClass<std::errc>, std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::formatter<MyClass<std::errc>>, std::basic_format_parse_context<char>>' requested here
 3475 |         requires __format::__formattable_with<_Tp, _Context>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3475:11: note: while substituting template arguments into constraint expression here
 3475 |         requires __format::__formattable_with<_Tp, _Context>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3743:31: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
 3743 |           basic_format_arg<_Context> __arg(__v);
      |                                      ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3754:12: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_S_make_elt<MyClass<std::errc>>' requested here
 3754 |         : _M_args{_S_make_elt(__a)...}
      |                   ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:3804:14: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_Arg_store<MyClass<std::errc>>' requested here
 3804 |       return _Store(__fmt_args...);
      |              ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/print:123:9: note: in instantiation of function template specialization 'std::format<MyClass<std::errc> &>' requested here
  123 |                  std::format(__fmt, std::forward<_Args>(__args)...));
      |                       ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/print:129:12: note: in instantiation of function template specialization 'std::println<MyClass<std::errc> &>' requested here
  129 |     { std::println(stdout, __fmt, std::forward<_Args>(__args)...); }
      |            ^
<source>:78:10: note: in instantiation of function template specialization 'std::println<MyClass<std::errc> &>' requested here
   78 |     std::println("{}", my_class); // Creates a compile error.
      |          ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/system_error:339:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const error_code' for 2nd argument
  339 |     operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)
      |     ^                                                ~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:570:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char' for 2nd argument
  570 |     operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
      |     ^                                                 ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:576:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char' for 2nd argument
  576 |     operator<<(basic_ostream<char, _Traits>& __out, char __c)
      |     ^                                               ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:587:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'signed char' for 2nd argument
  587 |     operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
      |     ^                                               ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:592:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'unsigned char' for 2nd argument
  592 |     operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
      |     ^                                               ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:601:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'wchar_t' for 2nd argument
  601 |     operator<<(basic_ostream<char, _Traits>&, wchar_t) = delete;
      |     ^                                         ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:606:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char8_t' for 2nd argument
  606 |     operator<<(basic_ostream<char, _Traits>&, char8_t) = delete;
      |     ^                                         ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:611:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char16_t' for 2nd argument
  611 |     operator<<(basic_ostream<char, _Traits>&, char16_t) = delete;
      |     ^                                         ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:615:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'char32_t' for 2nd argument
  615 |     operator<<(basic_ostream<char, _Traits>&, char32_t) = delete;
      |     ^                                         ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:668:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char *' for 2nd argument
  668 |     operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:681:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const signed char *' for 2nd argument
  681 |     operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:686:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const unsigned char *' for 2nd argument
  686 |     operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
      |     ^                                               ~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:695:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const wchar_t *' for 2nd argument
  695 |     operator<<(basic_ostream<char, _Traits>&, const wchar_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:700:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char8_t *' for 2nd argument
  700 |     operator<<(basic_ostream<char, _Traits>&, const char8_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:705:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char16_t *' for 2nd argument
  705 |     operator<<(basic_ostream<char, _Traits>&, const char16_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:709:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char32_t *' for 2nd argument
  709 |     operator<<(basic_ostream<char, _Traits>&, const char32_t*) = delete;
      |     ^                                         ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/bits/ostream.tcc:307:5: note: candidate function template not viable: no known conversion from 'const std::errc' to 'const char *' for 2nd argument
  307 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)
      |     ^                                                 ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/cstddef:125:5: note: candidate function template not viable: no known conversion from 'std::ostringstream' (aka 'basic_ostringstream<char>') to 'byte' for 1st argument
  125 |     operator<<(byte __b, _IntegerType __shift) noexcept
      |     ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:560:5: note: candidate template ignored: deduced conflicting types for parameter '_CharT' ('char' vs. 'std::errc')
  560 |     operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/string_view:760:5: note: candidate template ignored: could not match 'basic_string_view<_CharT, _Traits>' against 'std::errc'
  760 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/bits/basic_string.h:4077:5: note: candidate template ignored: could not match 'const basic_string<_CharT, _Traits, _Alloc>' against 'const std::errc'
 4077 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:621:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  621 |     operator<<(basic_ostream<wchar_t, _Traits>&, char8_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:626:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  626 |     operator<<(basic_ostream<wchar_t, _Traits>&, char16_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:630:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  630 |     operator<<(basic_ostream<wchar_t, _Traits>&, char32_t) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:651:5: note: candidate template ignored: could not match 'const _CharT *' against 'std::errc'
  651 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:715:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  715 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char8_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:720:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  720 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char16_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:724:5: note: candidate template ignored: could not match 'basic_ostream' against 'std::basic_ostringstream'
  724 |     operator<<(basic_ostream<wchar_t, _Traits>&, const char32_t*) = delete;
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:807:5: note: candidate template ignored: substitution failure [with _Ostream = std::ostringstream &, _Tp = std::errc]: constraints not satisfied for alias template '__rvalue_stream_insertion_t' [with _Os = std::basic_ostringstream<char> &, _Tp = std::errc]
  806 |     inline __rvalue_stream_insertion_t<_Ostream, _Tp>
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  807 |     operator<<(_Ostream&& __os, const _Tp& __x)
      |     ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:116:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__ostream_type &(*)(__ostream_type &)' (aka 'basic_ostream<char, std::char_traits<char>> &(*)(basic_ostream<char, std::char_traits<char>> &)') for 1st argument
  116 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:125:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__ios_type &(*)(__ios_type &)' (aka 'basic_ios<char, std::char_traits<char>> &(*)(basic_ios<char, std::char_traits<char>> &)') for 1st argument
  125 |       operator<<(__ios_type& (*__pf)(__ios_type&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:135:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'ios_base &(*)(ios_base &)' for 1st argument
  135 |       operator<<(ios_base& (*__pf) (ios_base&))
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:174:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long' for 1st argument
  174 |       operator<<(long __n)
      |       ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:178:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned long' for 1st argument
  178 |       operator<<(unsigned long __n)
      |       ^          ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:182:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'bool' for 1st argument
  182 |       operator<<(bool __n)
      |       ^          ~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:186:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'short' for 1st argument
  186 |       operator<<(short __n);
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:189:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned short' for 1st argument
  189 |       operator<<(unsigned short __n)
      |       ^          ~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:197:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'int' for 1st argument
  197 |       operator<<(int __n);
      |       ^          ~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:200:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned int' for 1st argument
  200 |       operator<<(unsigned int __n)
      |       ^          ~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:209:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long long' for 1st argument
  209 |       operator<<(long long __n)
      |       ^          ~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:213:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'unsigned long long' for 1st argument
  213 |       operator<<(unsigned long long __n)
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:228:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'double' for 1st argument
  228 |       operator<<(double __f)
      |       ^          ~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:232:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'float' for 1st argument
  232 |       operator<<(float __f)
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:240:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'long double' for 1st argument
  240 |       operator<<(long double __f)
      |       ^          ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:298:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'const void *' for 1st argument; take the address of the argument with &
  298 |       operator<<(const void* __p)
      |       ^          ~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:303:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'nullptr_t' (aka 'std::nullptr_t') for 1st argument
  303 |       operator<<(nullptr_t)
      |       ^          ~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:310:7: note: candidate function not viable: no known conversion from 'const std::errc' to 'const volatile void *' for 1st argument; take the address of the argument with &
  310 |       operator<<(const volatile void* __p)
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/ostream:336:7: note: candidate function not viable: no known conversion from 'const std::errc' to '__streambuf_type *' (aka 'basic_streambuf<char, std::char_traits<char>> *') for 1st argument
  336 |       operator<<(__streambuf_type* __sb);
      |       ^          ~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Compiler returned: 1

The full code and compile error can be seen at: https://gcc.godbolt./z/vq7WP3xnq

Share Improve this question edited Mar 16 at 19:17 Remy Lebeau 601k36 gold badges507 silver badges850 bronze badges asked Mar 16 at 15:29 sakcakocasakcakoca 1194 bronze badges 12
  • 4 Your code has undefined behavior. You should never specialize standard library functions or types without any user defined types. – guard3 Commented Mar 16 at 15:51
  • Your operator<< is in global namespace. However, the call is from inside namespace std. So the compiler searches namespace std for functions named operator<< - any functions, not necessarily those that match argument types - and only if it can't find any, then it would go up and search the enclosing namespace. Of course, there are plenty of operator<< overloads in namespace std; so yours in global namespace is never found. – Igor Tandetnik Commented Mar 16 at 16:24
  • The code is not modfying the std:: namespace since the custom operator<< overload is not defined in std namespace. How can it be an undefined behavior? @guard3 – sakcakoca Commented Mar 16 at 16:26
  • @sakcakoca The UB is not triggered by adding an operator<< overload to the global namespace. It's triggered by adding std::formatter<std::errc> specialization. Specializing a class template from standard library is allowed only when at least one parameter is a user-defined type. – Igor Tandetnik Commented Mar 16 at 16:28
  • 1 Yes, that's correct. Your operator<< won't actually be called from inside the standard library, and so won't unexpectedly alter the standard-specified behavior. Whereas your specialization might and might. This is what the rule is protecting against. – Igor Tandetnik Commented Mar 16 at 19:56
 |  Show 7 more comments

1 Answer 1

Reset to default 1

The issue revolves around argument-dependent lookup (ADL) and how it works differently in these two contexts.

It fails because:

  1. my_class.data() returns a const std::errc&

  2. std::errc is defined in the std namespace

  3. Your operator<< overload is in the global namespace

  4. ADL doesn't find your overload because it's not in the same namespace as std::errc

    The key insight: ADL only considers functions in the same namespace as the arguments.

    Your operator<< is in the global namespace, but std::errc is in the std namespace. ADL will only find operator overloads that are in the std namespace or in namespaces of classes that are arguments to the operator

    Move your overload to the std namespace:

namespace std {
inline std::ostream& operator<<(std::ostream& ostr, const std::errc& err) {
    return ostr << std::format("{}", err);
}
}
发布评论

评论列表(0)

  1. 暂无评论