3

I have no idea, why gcc compiles this code

#include <type_traits>

template<class Type, class ValueT>
class ImplAdd
{
   template<typename T>
   friend typename std::enable_if<std::is_same<T, ValueT>::value, Type>::type 
   operator+(T, T)
   {
      return Type{};
   }
};

enum class FooValueT { ONE, ZERO };

class Foo : ImplAdd<Foo, FooValueT>
{
public:
   Foo() {}
   Foo(FooValueT) {}
};

struct A {};

int main()
{
   Foo f = FooValueT::ONE + FooValueT::ZERO;
}

clang and msvc doesn't compile, and it seems to me, that they are right. Is it bug in GCC compiler? Version of gcc is 4.8.2.

Question is caused by my answer in question: In-class friend operator doesn't seem to participate in overload resolution, there is quote from standard in answer, that points, that such definition should be in class-scope, and if function is not template - gcc reject this code, that is right. Thanks for answers, and quotes from standard, that proves, that gcc is right (or not) are very appreciated.

Community
  • 1
  • 1
ForEveR
  • 55,233
  • 2
  • 119
  • 133

1 Answers1

3

I would say GCC accepts this incorrectly. Quoting C++11, emphasis mine:

Namespace membership, 7.3.1.2/3

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). ...

Argument-dependent lookup, 3.4.2/2:

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:

  • ...
  • If T is an enumeration type, its associated namespace is the namespace in which it is defined. If it is class member, its associated class is the member’s class; else it has no associated class.
  • ...

3.4.2/4:

When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2) except that:

  • ...
  • Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.3).
  • ...

Based on the above, I reason that FooValueT (the type of FooValueT::ONE and FooValueT::TWO) has :: as an associated namespace, but has no associated classes (since it's an enumeration). Therefore, friend functions defined in class template ImplAdd should not be considered during ADL.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    The first quote isn't really relevant here. You'd probably want 7.3.1.2 [namespace.memdef]/p3: "If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship)." – T.C. Dec 11 '14 at 12:30
  • @T.C. Good find, thanks! I knew there was a passage which addressed this more directly, but I couldn't remember where. I'll add it. – Angew is no longer proud of SO Dec 11 '14 at 12:49