Help Required: Operator overloaded function template and Friend

C

CoolPint

After upgrading to gcc 3.4.2 from gcc 3.2.3, I got compiler errors
that I could not figure out. After reading other postings, I learned
that my coding was not compliant to the standard in the first place
and I did fix many of them, especially with the proper use of the
keyword "typename".

But I have one problem I have no idea how to fix. I created below a
simpler coding which demonstrates my problem:

The coding below used to compile and run fine under gcc 3.2.3, but
under 3.4.2, it doesn't compile with the error message "no match for
'operator==' in 'a==b'

What I read is that 3.4.2 is more compliant to the standard so I
believe it's my coding which is wrong, but I cannot figure out what is
wrong.

My guessing is that for some reason, the compiler doesn't "use" the
operator function template to instantiate a function to serve 'a ==
b'.

If I modify the friend declaration to include the definition of the
function inline, then it works as I guessed since each instantiation
of the class template will instantiate a corresponding global friend
function.

I will be very grateful if someone can kindly explain what the problem
is and how to fix the problem without making the function template
into an inlined global friend function I described above.

Thank you very much in advance.

template <typename T>
class Obj {
public:
class Sub;
};

template <typename T>
bool operator==(const typename Obj<T>::Sub & a, const typename
Obj<T>::Sub & b)
{ return a.n == b.n; }

template <typename T>
class Obj<T>::Sub {
friend bool operator==<T>(const Obj<T>::Sub &, const Obj<T>::Sub &);
public:
Sub(int i):n(i) { }
private:
int n;
};

#include <iostream>
int main()
{
Obj<int>::Sub a(10),b(10);
if ( a == b )
std::cout << "OK" ;
}
 
V

Victor Bazarov

CoolPint said:
[...]
template <typename T>
bool operator==(const typename Obj<T>::Sub & a, const typename
Obj<T>::Sub & b)
{ return a.n == b.n; }

I think you've run into one of those "non-deducible contexts". The T is
too deeply entrenched into the 'a' and 'b' arguments here for the compiler
to figure out from real arguments (operands) you give it.
[..]
#include <iostream>
int main()
{
Obj<int>::Sub a(10),b(10);
if ( a == b )
std::cout << "OK" ;
}

There are probably several ways to fix it and one would be to make your
operator== a member:

template <typename T>
class Obj<T>::Sub {
public:
Sub(int i):n(i) { }
bool operator==(const Obj<T>::Sub &b) const
{
return n == b.n;
}
private:
int n;
};

Another way is to call the operator function using the functional notation
with the template argument explicit:

...
if (operator==<int>(a,b))
...

V
 
C

CoolPint

Thank you for your help. I did think about making it a member
function, but I was hoping to take advantage of implicit conversions.
In fact, what I didn't show in my original posting was another problem
where I couldn't get the implicit conversion work. It's more likely
below where there are two nested
classes and there is an implicit conversion from Sub2 to Sub1, but not
vice versa. And I thought by making it a global function template, I
could take advantage of the implicit conversion as well, but it
doesn't work on 3.4.2 either.

template <typename T>
class Obj {
public:
class Sub1;
class Sub2;
};

Thank you for your tip on using the functional notation, and probably
I will fix my problem that way until I find a better solution, but it
does seem to defeat the purpose of using operator overloading in the
first place.

I hope to get more insight to the problem by asking following
questions if you don't mind. I apologise in advance if this is not the
right group for this kind of questions.

1. It used to work with gcc 3.2.3 but not with 3.4.2 anymore. What is
the correct behaviour according to the standard? What is the rule
governing "deducible contexts" that I should study? I also wonder what
other compilers do with this situation since I have only gcc.

2. If it's correct not to be able to deduce T in this case according
to the standard, then any function template which accepts a nested
type of a class template whose type parameter needs to be deduced from
the funtion call arguments would never work, would it? Am I
understanding correctly?

Can some experts kindly provide answers to my questions above? Thank
you very much in advance.
I think you've run into one of those "non-deducible contexts". The T is
too deeply entrenched into the 'a' and 'b' arguments here for the compiler
to figure out from real arguments (operands) you give it.
[..]
#include <iostream>
int main()
{
Obj<int>::Sub a(10),b(10);
if ( a == b )
std::cout << "OK" ;
}

There are probably several ways to fix it and one would be to make your
operator== a member:

template <typename T>
class Obj<T>::Sub {
public:
Sub(int i):n(i) { }
bool operator==(const Obj<T>::Sub &b) const
{
return n == b.n;
}
private:
int n;
};

Another way is to call the operator function using the functional notation
with the template argument explicit:

...
if (operator==<int>(a,b))
...

V
 
V

Victor Bazarov

CoolPint said:
[...]
I hope to get more insight to the problem by asking following
questions if you don't mind. I apologise in advance if this is not the
right group for this kind of questions.

It's the right newsgroup.
1. It used to work with gcc 3.2.3 but not with 3.4.2 anymore. What is
the correct behaviour according to the standard?

AFAIK, the inability to deduce T is the correct behaviour. What may have
happened is that g++ provided deduction in some cases (where it didn't have
to) and in other, similar, cases it could not deduce arguments or did it
incorrectly. That would certainly be a bug and to fix it they decided to
follow the Standard requirements precisely.

To know more about the differences between 3.2.3 and 3.4.2, please visit
their web site or post to gnu.g++.help.
What is the rule
governing "deducible contexts" that I should study?

The rules are defined in the C++ Standard document. It's not a very good
studying tool though. You should probably get a copy of "C++ Templates"
by Vandevoorde and Josuttis.
I also wonder what
other compilers do with this situation since I have only gcc.

Comeau refuses to compile stating the same reason, essentially. I leave
it to others to test more compilers.
2. If it's correct not to be able to deduce T in this case according
to the standard, then any function template which accepts a nested
type of a class template whose type parameter needs to be deduced from
the funtion call arguments would never work, would it? Am I
understanding correctly?

Yes, AFAICT. The reason is that the "nested" type is _dependent_ on
the template argument.
Can some experts kindly provide answers to my questions above?

I am sure they will, in addition to my feeble attempt.
Thank
you very much in advance.

And I'd like to ask for a favour. Please don't top-post next time.
Thanks!
I think you've run into one of those "non-deducible contexts". The T is
too deeply entrenched into the 'a' and 'b' arguments here for the
compiler
to figure out from real arguments (operands) you give it.
[..]
#include <iostream>
int main()
{
Obj<int>::Sub a(10),b(10);
if ( a == b )
std::cout << "OK" ;
}

There are probably several ways to fix it and one would be to make your
operator== a member:

template <typename T>
class Obj<T>::Sub {
public:
Sub(int i):n(i) { }
bool operator==(const Obj<T>::Sub &b) const
{
return n == b.n;
}
private:
int n;
};

Another way is to call the operator function using the functional
notation
with the template argument explicit:

...
if (operator==<int>(a,b))
...

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top