c++clangoperator-overloadingbitwise-operatorsg++-4.7

C++ friend operator overloading with template parameters for nested enums


I ran into an issue while overloading friend bitwise operator with template parameters for nested enums.

Originally I ran into the issue with proprietary embedded C++ compiler. After that I tried gcc, clang and MSVC and got exactly the same failure. Except, any GCC version prior to 5.0 that I have tried is able to compile example code. Choice of the C++ standard with any compiler does not seem to make any difference.

Here is an example:

struct Complex
{
   struct Nested1
   {
       enum ENUM
       {
          Value1_1,
          Value1_2
       };
   };

   struct Nested2
   {
       enum ENUM
       {
          Value2_1,
          Value2_2
       };
   };

   template<typename EnumL, typename EnumR>
   friend Complex operator|(const EnumL dummyL, const EnumR dummyR)
   {
        Complex dummyComplex;
        return dummyComplex;
   }
};

int main()
{
   Complex complex = Complex::Nested1::Value1_1 | Complex::Nested2::Value2_2;
}

Compiler does not see operator |. Code compiles if I move operator overloading just below the structure definition. Although it is not a friend function anymore.

However, a little different code example compiles without any issues.

struct Complex
{
    enum ENUM1
    {
        Value1_1,
        Value1_2
    };

    enum ENUM2
    {
        Value2_1,
        Value2_2
    };

    template<typename EnumL, typename EnumR>
    friend Complex operator|(const EnumL dummyL, const EnumR dummyR)
    {
        Complex dummyComplex;
        return dummyComplex;
    }
};

int main()
{
   Complex complex = Complex::Value1_1 | Complex::Value2_2;
}

What am I missing here?


Solution

  • The reason is that in the first example the operator| is not in the class inside which the enum types are defined. Hence the function is not being found by ADL (see point 4 of the 2nd list on that page):

    For arguments of enumeration type, the innermost enclosing namespace of the declaration of the enumeration type is defined is added to the set. If the enumeration type is a member of a class, that class is added to the set.

    In the quote above "set" is defined as "the associated set of namespaces and classes" that are used to look up the function.

    Hence in the 1st example no operator| is found which returns a Complex type.

    When you move the operator| overload outside of the Complex class in the 1st example, you move it into the global namespace. The global namespace is always part of the "set" mentioned above, and hence the overload is found.

    In the 2nd example the enums and the operator| function are in the same class, so they are found by ADL.