views:

216

answers:

3

Hi,

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 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.

+4  A: 

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.

sbi
It's not the syntax itself that I'm asking about, but its difference from what I would guess it should be - from looking at the rest of the language. Perhaps you disagree, which is - as you said yourself - fine.
rmn
I think the `friend` syntax fits well with other similar constructs like template definitions and declarations (`template<class S> class A;`) and template template parameters (`template<template<class S> class A> class C {};`).
Georg Fritzsche
+3  A: 

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<>.

Alexander Gessler
Your answer to question two seems reasonable, thanks. However, the syntax of question 1 seems pretty much off to me.. Even more so since the template argument (typename S) isn't used in the friend decleration at all.
rmn
In addition, i believe that this system is necessary: It determines the type of a template parameter while still being dependent. I.e a parameter type has a template-clause nesting and a position in that clause: `template<typename A, typename B> template<typename C>` in wich `A` is `#dependent-0-0` and `B` is `#dependent-0-1` and `C` is `#dependent-1-0`. While i've never implemented a C++ compiler, i believe that we need this unambiguous schema to associate out-of-class definitions with the template that they belong to.
Johannes Schaub - litb
+2  A: 

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.

Omnifarious
The line 'template <typename S>' would define the relevant S, I don't understand your point. It is just the same as if I would define two clashing names anywhere in the class.. You explanation to 1 is very reasonable though, thanks.
rmn
@sbi, thanks! I fixed it. @rmn, I updated the answer to take your comment into consideration.
Omnifarious