9

I have a following nested template class inside another template class:

template<typename T>
struct A
{
    template<typename V>
    struct B {};
};

What would be the signature of a non-member operator== for the nested type B? The following naïve attempt does not work:

template<typename T, typename V>
bool operator==(A<T>::B<V> left, A<T>::B<V> right);

Clang, GCC and MSVC gives various different errors and/or hints what is wrong such as missing template keyword but none of my attempts to resolve it worked out.

Note that this obviously works:

template<typename T>
struct A
{
    template<typename V>
    struct B {};

    template<typename V>
    friend bool operator==(B<V> left, B<V> right)
    {
        return true;
    }
};

However the reason I need the out of line non-member declaration is to document it using qdoc. The qdoc is using clang to parse the sources and it requires me to provide the declaration of the operator== that I have actually implemented in place like just shown.

LIVE DEMO

Resurrection
  • 3,716
  • 2
  • 31
  • 50

2 Answers2

0

The error is not too far off, as you do need the template keyword, but also typename to denote dependent type. A working example would be of the form:

template <typename T, typename V>
bool operator==(typename A<T>::template B<V> left,
                typename A<T>::template B<V> right) {...}

Although I would suggest instead:

template <typename T, typename V>
using operator_type = typename A<T>::template B<V>;

template <typename T, typename V>
bool operator==(operator_type<T, V> left,
                operator_type<T, V> right) {...}

as a means of mitigating some of the esoteric syntax required. It's one of those weird one off things where you would expect typename to be enough to denote that ::B is a dependent name of A, but you still need the template keyword, because the parser gets notoriously confused when dealing with < and >. This answer does a pretty nice job of explaining why:

After name lookup (3.4) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.

Now we are back to the same problem as with typename. What if we can't know yet whether the name is a template when parsing the code? We will need to insert template immediately before the template name, as specified by 14.2/4. This looks like:

t::template f<int>(); // call a function template

Community
  • 1
  • 1
jfh
  • 153
  • 13
-1

You can have an inline friend declaration and an outline definition

template<typename T>
struct A
{
    template<typename V>
    struct B
    {
        friend bool operator==(B left, B right);
    };
};

template <typename T, typename V>
bool operator==(typename A<T>::template B<V> left, typename A<T>::template B<V> right)
{
    return true;
}

However gcc warns

warning: friend declaration bool operator==(A<T>::B<V>, A<T>::B<V>) declares a non-template function [-Wnon-template-friend]

note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)

And to fix that warning we would have to forward declare operator==(B left, B right) before the definition of B, which can only be inside A, which would force it to be a friend of A also.

Caleth
  • 45,483
  • 2
  • 38
  • 67
  • It does not work for me either. It does indeed complain as you point out about non-member being declared (Dan Saks on that topic: https://www.youtube.com/watch?v=POa_V15je8Y) but the non-member definition is not actually used. It suffers from the same problem as what was proposed in the OP's comments (now deleted). Forward declaring it as you suggest and adding `<>` to the friend declaration complains about no matching template. See here: https://godbolt.org/z/O5KNu6 – Resurrection Oct 18 '18 at 10:53
  • Worth noting is that when trying out with Clang it points to the same underlying issue we are all seeing in all our attempts: `"Cannot infer template type T"`. – Resurrection Oct 18 '18 at 10:55