A C++ syntax question involving non trivial templating and friend declaration
The following code should be self explanatory. I have two questions regarding the used syntax (which is the syntax that must be used). I'll be forever grateful if you could provide me with answers for these presented questions.
template <typename T>
struct A {
template <typename S>
void f (const A<S> &s);
template <typename S>
friend struct A;
// Question 1: why isn't the syntax 'friend struct A<S>' ?
// The semantic would stay, since we would like A<S> (for ANY S) to be friend of every A<T>..
private:
void g () const {}
};
template <typename T>
template <typename S> // Question 2: Why开发者_如何学JAVA can't the syntax be 'template <typename T, typename S>' ?
void A<T>::f (const A<S> &s) {
s.g();
}
int main () {
A<bool> abool;
A<char> achar;
abool.f(achar);
}
I have verified that indeed this is the only correct syntax (I'll be happy to find out I'm mistaken). My questions are more regarding the reasoning behind the syntax, as explained in the questions' body.
Thanks for any help.
why isn't the syntax ...
Why can't the syntax be ...
What do you expect us to say? Whoever decided this syntax (mostly Stroustrup himself, AFAIK) thought their syntax better than yours.
Which one is nicer or easier to remember I wouldn't know - but I do find theirs making more sense than yours. You're free to disagree, of course.
Edit: OK, Alexander has nicely answered question #2. About #1:
The difference is that A<S>
names a type, which is what is expected for a function argument, while A
by itself is the name of a template, from which types are to be created, which makes sense if you want to befriend a template instead of a type:
template <typename S>
void f (const A<S> &s); // A<S> being the name of a type
template <typename S>
friend struct A; // A being the name of a template
You can befriend a specific template instance instead of the whole template, but for that the template must already be known by the compiler (i.e., declared) at the friend
declaration:
template< typename T >
class foo;
class bar {
friend class foo<int>; // foo<int> being the name of a specific instance of foo
};
So befriending a template is an exception (the "usual" friend
declarations declares a function or class) and does need the differing syntax.
Albeit I can't say why this syntax was choosen, I can just say that I would support both decisions made by the language designers - they make sense to me. In Question 2, you have not only one template, you have two nested templating levels. Why should the syntax to define a template member of a template class hide this fact? This way, it's just a re-combination of existing template syntax, while yours would require special rules to merge the template arguments for nested templates in one template<>
.
Suppose your nested template declaration were just a little more complicated:
template <typename T, int I>
struct A {
template <typename S, I>
void f (const A<S, I> &s);
template <typename S, int J>
friend struct A;
// Question 1: why isn't the syntax 'friend struct A<S, J>' ?
// The semantic would stay, since we would like A<S, J> (for ANY S, J combination) to be friend of every A<T, I>..
private:
void g () const {}
};
template <typename T, int I>
template <typename S> // Question 2: Why can't the syntax be 'template <typename T, int I, typename S>' ?
void A<T>::f (const A<S> &s) {
s.g();
}
int main () {
A<bool, 5> abool;
A<char, 7> achar;
abool.f(achar);
}
Suddenly your suggestion doesn't seem so reasonable or obvious anymore. Which set of arguments goes first? Suppose you were using C++0x and had a variable argument list?
As for the friend declaration, by having the syntax you propose (friend struct <S, J>
), you suddenly make the compiler have to deduce that S
and J
are meant as template arguments and should not be grabbed from some random scope. Suppose someone introduced a type S
at the same scoping level as struct A
? Which S
would the declaration friend struct A<S,J>
refer to? How would the compiler know? Is it reasonable to have the introduction of a name in an outer scope to radically change the meaning of a declaration in a nested scope?
And if you meant that the declaration should read, in total: template <typename S, int J> friend struct A<S, J>
, then why should a friend forward template declaration look any different from a standard template declaration? It's redundant to include the names of the template variables after the name of the template when you already mention them in the template <typename S, int J>
part.
I also think your suggested syntax would make figuring out what a template specialization is doing a lot harder because you'd have to look at more parts of the code and correlate things.
As an example, a template specialization of my version of A
would look like this:
template <typename T>
struct A<T, 5> {
};
And as you can see, this is strangely close to your suggested friend forward declaration syntax, and it might be hard to tell whether or not you're intending to specify a specialized version or not. It would require matching up template arguments with template parameters whereas in the way it's currently done, if you have no template parameters you aren't talking about a specialization.
精彩评论