34

Given the following code:

#include <iostream>

struct Alice
{
  template <typename A>
  void operator|(const A& /*a*/) const
  {
    std::cout << "operator| member" << std::endl;
  }
};

template <typename A>
void operator|(const A& /*a*/, const Alice& /*alice*/)
{
  std::cout << "operator| non-member" << std::endl;
}

int main()
{
  Alice a;
  Alice b;

  a | b;

  return 0;
}

It compiles without warning with both GCC 4.8.1, 4.9 and clang 3.4, but gives different results.

$ g++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| non-member

$ clang++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| member

What causes this difference? How can I force the same behaviour?

EDIT: Interesting fact: removing the const qualifier from the member function makes gcc prefer the member function also. It does not solve the problem, however.

EDIT: clang++ prefers the non-member instead, if -std=c++11 is not specified.

EDIT: ICC 14.0 prefers non-member, no warning emitted.

erenon
  • 18,838
  • 2
  • 61
  • 93
  • 17
    Visual Studio 2013, for its part, *errors out* with "operator| is ambiguous". – zneak May 22 '14 at 19:39
  • 2
    gcc 4.9.0 returning same result as gcc 4.8.1. – Brian Bi May 22 '14 at 19:43
  • 11
    As far as I can tell, those overloads should both be equally good, and so this should be rejected as ambiguous. Have you checked the various bug trackers perhaps for known issues? – Kerrek SB May 22 '14 at 20:03
  • Uhhh I think this is a dup, but it'll be hard to find the other question with the same observation. IIRC gcc has a bug report that they rank member functions too low or something along those lines. – dyp May 22 '14 at 20:08
  • 2
    I can't [reproduce](http://coliru.stacked-crooked.com/a/d7335c3b8cf350d6) the behavior you're seeing with clang. Compiled in C++03 mode clang behaves the same as gcc, but compiled in C++11 mode it errors out with the message the operator call is ambiguous. – Praetorian May 22 '14 at 20:14
  • @Praetorian: My clang is: Ubuntu clang version 3.4-1ubuntu1 (trunk) (based on LLVM 3.4). No idea what else could make difference there. – erenon May 22 '14 at 20:19
  • Relared? http://stackoverflow.com/q/22547503/420683 – dyp May 22 '14 at 20:21
  • 5
    Note there's been a related [clang bug](http://llvm.org/bugs/show_bug.cgi?id=17075). Couldn't find anything about g++, though. – dyp May 22 '14 at 20:31
  • 1
    so actually Visual Studio got it right over both gcc and clang? That's a first. – bolov May 23 '14 at 16:46
  • @bolov: actually, it depends on the clang build. However, gcc accepts it with a warning event without templates. – erenon May 23 '14 at 16:49
  • @kerrekSB Based off gut feeling (mine agrees, they should be the same), or off the overload resolution rules in the standard? If the second, care to walk us through why they are equal? – Yakk - Adam Nevraumont May 26 '14 at 14:36
  • @Yakk: based off the standard, but a walkthrough wouldn't fit into this margin... – Kerrek SB May 26 '14 at 14:39

1 Answers1

8

According to overload resolution, there are two viable functions: The specialization of the global operator|-template with deduced arguments and the specialization of the member operator function template. Both have the same signature - the member function template has an implicit object parameter of type Alice const& (see §13.3.1/4).

So both viable functions (after template argument deduction) have the same signature. And neither of the templates from which they were instantiated is more specialized than the other. So this is indeed an ambiguity and therefore ill-formed. Surprisingly, VC++ is correct.

How can I force the same behaviour?

Perhaps you should just remove the ambiguity, Clang, VC++ and GCC should have the same behavior then.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 1
    Thanks for summarizing the situation. This is indeed an example only, to prove the compiler misbehaving. Original code is much more complex: https://github.com/erenon/pipeline (However, buggy code is not committed, but one can get the idea why I'm fiddling with operator|s) – erenon May 27 '14 at 16:04
  • It's already "corrected". (Or rather, it works as I want it to work) – erenon May 27 '14 at 16:25