I have the following class structure:
template <typename...>
class SomeClass {
public:
class Foo { };
class Bar { };
};
I need to define operator== for SomeClass<Ts...>::Foo and SomeClass<Ts...>::Bar and I need to make it a friend of both Foo and Bar. The closest I got this to working is the following:
template <typename...>
class SomeClass {
public:
class Bar;
class Foo {
friend bool operator==(const Foo&, const Bar&) {
return true;
}
};
class Bar {
friend bool operator==(const Foo&, const Bar&);
};
};
Then I do:
SomeClass<int, double>::Foo foo;
SomeClass<int, double>::Bar bar;
foo == bar;
This compiles and works fine except for the fact that gcc gives me the warning:
warning: friend declaration `bool operator==(const SomeClass<Args>::Foo&, const SomeClass<Args>::Bar&)` declares a non-template function
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
I kind of understand why this happens (operator== indeed depends on the template parameters of SomeClass), but how do I get rid of it? Adding <> to the second friend declaration, as suggested, only breaks compilation: the friending is no longer recognized and the compiler complaints about access to a private member.
I tried to modify the code according to the template friend declaration guide, but that only made things worse:
template <typename...>
class SomeClass;
template <typename... Args>
bool operator==(const typename SomeClass<Args...>::Foo&,
const typename SomeClass<Args...>::Bar&);
template <typename... Args>
class SomeClass {
public:
class Bar;
class Foo {
friend bool operator==<Args...>(const Foo&, const Bar&);
};
class Bar {
friend bool operator==<Args...>(const Foo&, const Bar&);
};
};
template <typename... Args>
bool operator==(const typename SomeClass<Args...>::Foo&,
const typename SomeClass<Args...>::Bar&) {
return true;
}
Now the weirdest thing happens when I call foo == bar:
error: no match for ‘operator==’ (operand types are ‘SomeClass<int, double>::Foo’ and ‘SomeClass<int, double>::Bar’)
note: candidate: ‘bool operator==(const typename SomeClass<Args ...>::Foo&, const typename SomeClass<Args ...>::Bar&) [with Args = {}; typename SomeClass<Args ...>::Foo = SomeClass<>::Foo; typename SomeClass<Args ...>::Bar = SomeClass<>::Bar]’ (reversed)
note: no known conversion for argument 1 from ‘SomeClass<int, double>::Bar’ to ‘const SomeClass<>::Foo&’
I.e. for some weird reason it tries to call the template specification with an empty arguments list and fails. Changing the operator type (to operator+) doesn't help (my idea was, this had something to do with C++20 new rules for comparison operators, but no).
So the questions I have are:
- What's going on? I'm kind of confused, especially by the second part: why would the compiler try to call the operator for an empty parameter pack?
- How do I avoid the warning in the first solution?
- How do I define the operator outside of the class definition properly?