Discussion:
Template friendship to nested template class
(too old to reply)
Mycroft Holmes
2009-09-15 12:42:22 UTC
Permalink
Hi everyone.

I remember that template friendship is a hot topic, because it's not
completely clear in the standard, but we recently discovered a piece
of code like this:


template <typename T>
class OUTER
{
int private_;

template <long A, long B>
struct INNER
{
int doIt(OUTER<T> x) {return x.private_; }
};


template <long A, long B>
friend struct INNER;

public:
int exec() { return INNER<3,4>().doIt(*this); }
};


int main()
{
OUTER<double> x;
return x.exec();
}



Both VC and gcc are happy with this code, but the intel compiler is
not, and in fact Comeau reports a detailed reason for the failure:
(btw, Comeau reports 3 errors, but only the first is meaningful; the
latest intel compiler v11 only reports the 2 irrelevant errors,
omitting the first...)


Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 14: error: template nesting depth does not match
the previous
declaration of class template "OUTER<T>::INNER [with
T=double]"
Probably you are missing an EXTRA template <Param> to declare
a nested friend,
where Param is not the same id used for the enclosing class
but matches it when instantiated
friend struct INNER;
^
detected during instantiation of class "OUTER<T> [with
T=double]" at
line 29


Can somebody explain what would be the "EXTRA template <Param> to
declare a nested friend"?
(note that we don't need a fix, we already reworked the code: we took
INNER outside OUTER and made "doIt" a template member function)
Victor Bazarov
2009-09-15 12:58:18 UTC
Permalink
Post by Mycroft Holmes
I remember that template friendship is a hot topic, because it's not
completely clear in the standard, but we recently discovered a piece
template <typename T>
class OUTER
{
int private_;
template <long A, long B>
struct INNER
{
int doIt(OUTER<T> x) {return x.private_; }
};
template <long A, long B>
friend struct INNER;
int exec() { return INNER<3,4>().doIt(*this); }
};
int main()
{
OUTER<double> x;
return x.exec();
}
Both VC and gcc are happy with this code, but the intel compiler is
(btw, Comeau reports 3 errors, but only the first is meaningful; the
latest intel compiler v11 only reports the 2 irrelevant errors,
omitting the first...)
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions
"ComeauTest.c", line 14: error: template nesting depth does not match
the previous
declaration of class template "OUTER<T>::INNER [with
T=double]"
Probably you are missing an EXTRA template <Param> to declare
a nested friend,
where Param is not the same id used for the enclosing class
but matches it when instantiated
friend struct INNER;
^
detected during instantiation of class "OUTER<T> [with
T=double]" at
line 29
Can somebody explain what would be the "EXTRA template <Param> to
declare a nested friend"?
(note that we don't need a fix, we already reworked the code: we took
INNER outside OUTER and made "doIt" a template member function)
I think it's because the name referred to in a 'friend' declaration is
looked up (or presumed) to exist at the namespace scope. So, if you
consider that your 'OUTER' template is in the global namespace, then the
'friend' declaration you have refers to the template '::INNER', and not
to '::OUTER::INNER'.

I vaguely recall that in early discussions on the defects of C++
Standard there was a mention that nested classes should be granted full
access to the containing class since they are, in fact, members, and by
definition 'private' is for *members* and friends only. I don't know
what the resolution was, however. Perhaps a careful look at the current
draft Standard for C++0x would reveal what to expect. I would think
that if it was determined a defect, then in the situation like yours
there would be no need for a 'friend' declaration - 'INNER' already has
access to 'OUTER's private members.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Mycroft Holmes
2009-09-15 13:59:17 UTC
Permalink
Post by Victor Bazarov
Post by Mycroft Holmes
Can somebody explain what would be the "EXTRA template <Param> to
declare a nested friend"?
I think it's because the name referred to in a 'friend' declaration is
looked up (or presumed) to exist at the namespace scope.  So, if you
consider that your 'OUTER' template is in the global namespace, then the
'friend' declaration you have refers to the template '::INNER', and not
to '::OUTER::INNER'.
Your conjecture is exactly what we thought (that's also the motivation
for the workaround... actually the signature of INNER is (typename)
OUTER<T>::(template) INNER.
However we tried a lot of syntax variations and we cannot figure out
what Comeau would expect... his hint seems to imply that we should add
some extra template <...> statement, but we were unable to make it
right.
Post by Victor Bazarov
I don't know
what the resolution was, however.  
indeed, I remember lots of advice against mixtures of "template" and
"friendship", because often some harmless-looking syntax turns out to
be non-portable and in fact most compilers disagree.
Post by Victor Bazarov
that if it was determined a defect, then in the situation like yours
there would be no need for a 'friend' declaration - 'INNER' already has
access to 'OUTER's private members.
I agree this would be the best solution: otherwise there would be no/
negligible difference between a nested and a global class (except,
maybe, access control)

Many thanks for sharing your thoughts, anyway,
MH
Igor Tandetnik
2009-09-15 16:55:47 UTC
Permalink
Post by Mycroft Holmes
template <typename T>
class OUTER
{
int private_;
template <long A, long B>
struct INNER
{
int doIt(OUTER<T> x) {return x.private_; }
};
template <long A, long B>
friend struct INNER;
int exec() { return INNER<3,4>().doIt(*this); }
};
int main()
{
OUTER<double> x;
return x.exec();
}
Both VC and gcc are happy with this code, but the intel compiler is
(btw, Comeau reports 3 errors, but only the first is meaningful; the
latest intel compiler v11 only reports the 2 irrelevant errors,
omitting the first...)
First, I'd like to point you to DR45:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45

The resolution is that a nested class is a member of its enclosing
class, and has the same access as any other member. So the friend
declaration is not required.

While not officially part of the standard, I believe most modern
compilers incorporate DR45. In particular, Comeau compiles your example
with the friend declaration removed completely.


Second, your example as written appears to be invalid for reasons Victor
has pointed out: your friend declaration refers to ::INNER, not to
OUTER::INNER. However these forms don't compile either:

template <long A, long B>
friend struct OUTER::INNER;

template <long A, long B>
friend struct OUTER<T>::INNER;

I'm not sure why - it looks like these should work. Anyway, as a
workaround, this compiles (at least with Comeau, I didn't test with
others):

template <typename U>
template <long A, long B>
friend struct OUTER<U>::INNER;

This doesn't quite do the same thing - it grants a broader friendship
than originally intended - but may be good enough.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
Mycroft Holmes
2009-09-16 06:56:48 UTC
Permalink
Post by Igor Tandetnik
The resolution is that a nested class is a member of its enclosing
class, and has the same access as any other member. So the friend
declaration is not required.
While not officially part of the standard, I believe most modern
compilers incorporate DR45. In particular, Comeau compiles your example
with the friend declaration removed completely.
thanks, this is good news.
Post by Igor Tandetnik
template <long A, long B>
friend struct OUTER<T>::INNER;
...
I'm not sure why - it looks like these should work.
yes, this is in fact one of the many "variations" we tried.
Post by Igor Tandetnik
template <typename U>
template <long A, long B>
friend struct OUTER<U>::INNER;
This doesn't quite do the same thing
I think we didn't try it for the reason you mention.
Anyway I think the most elegant option is to remove the friend
declaration entirely.

Thanks for your help
MH

Continue reading on narkive:
Search results for 'Template friendship to nested template class' (Questions and Answers)
54
replies
What is the true religion?
started 2020-06-25 13:24:48 UTC
religion & spirituality
Loading...