Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ ambiguous member vs non-member function template resolution in GCC 14 but not in prior GCC versions

Tags:

c++

gcc

sfinae

The following code compiles fine with GCC 13 and earlier, but GCC 14.1 produces an "ambiguous overload" error. Is it a compiler or code problem, and, more pragmatically, can I make the compiler prefer the non-member template by making changes in namespace ns (staying in the c++11 land)?

#include <iostream>
#include <sstream>
#include <string>

//=======================================================================
namespace ns {
  struct B
  {
    std::ostringstream os_;

    ~B()
    {
      std::cerr << os_.str() << '\n';
    }
  
    template<typename T>
    B& operator<<(T const& v)
    {
      this->f(v);
      this->os_ << ' ';
      return *this;
    }

  private:
    void f(int v) { os_ << v; }
    void f(std::string const& v) { os_ << "\"" << v << '\"'; }
  };

  struct D : public B
  {};
}
//==============================================================
namespace nsa {
  struct A
  {
    int i;
    std::string s;
  };

  template<typename S>
  S& operator<<(S&& s, A const & a)
  {
    s << "S<<A" << a.i << a.s; 
    return s;
  }
}
//==============================================================
int main()
{
  ns::D() << "XX" << nsa::A{1, "a"};
}

GCC 13 compiles it successfully and the program output is

"XX" "S<<A" 1 "a"  

The GCC 14 compiler output:

In function 'int main()':
<source>:50:19: error: ambiguous overload for 'operator<<' (operand types are 'ns::B' and 'nsa::A')
   50 |   ns::D() << "XX" << nsa::A{1, "a"};
      |       ~~~~~~~~~~~ ^~      ~~~~~~~~~
      |           |               |
      |           ns::B           nsa::A
<source>:17:8: note: candidate: 'ns::B& ns::B::operator<<(const T&) [with T = nsa::A]'
   17 |     B& operator<<(T const& v)
      |        ^~~~~~~~
<source>:41:6: note: candidate: 'S& nsa::operator<<(S&&, const A&) [with S = ns::B&]'
   41 |   S& operator<<(S&& s, A const & a)
      |      ^~~~~~~~

I thought the absence of other B::f() would lead to a substitution failure, taking the member operator<<() template out of the overload set. Multiple versions of clang think that it's an ambiguous overload. MSVC seems to try to convert A to an int or to a string as if it doesn't see the non-member template at all, and outputs something like

<source>(22): error C2664: 'void ns::B::f(const std::string &)': cannot convert argument 1 from 'const T' to 'int'
        with
        [
            T=nsa::A
        ]
<source>(22): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
<source>(53): note: see reference to function template instantiation 'ns::B &ns::B::operator <<<nsa::A>(const T &)' being compiled
        with
        [
            T=nsa::A
        ]

The answer to this question seems to explain the ambiguity, although there are no calls to non-existing functions there, so I would not expect SFINAE to kick in in that example.
Adding an enable_if to the member template does not seem to work very well, because there can be types convertible to int for which one may want to optionally define a non-member template.

like image 361
akryukov Avatar asked Oct 19 '25 11:10

akryukov


1 Answers

Is it a compiler or code problem?

A code problem.

Diagnostic of gcc 14 is pretty clear With

ns::D() << "XX" << nsa::A{1, "a"};
//              ^^

We have "ns::B& << nsa::A" (ns::D is "lost" with ns::D() << "XX" which returns ns::B)

and we have 2 equally overloads (exact match)

  • ns::B::operator<< (const T&) with T == nsa::A
  • nsa::operator<< (T&&, const A&) with T == ns::B&

neither is more specialized than the other.

SFINAE doesn't happens on body.

You might apply SFINAE, which remove ns::B::operator<< from viable function, removing ambiguity for your case:

template<typename T>
auto operator<<(T const& v)
-> decltype(this->f(v), *this)
{
  this->f(v);
  this->os_ << ' ';
  return *this;
}

Demo

like image 76
Jarod42 Avatar answered Oct 22 '25 00:10

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!