3

以下代码应该是不言自明的。关于使用的语法(这是必须使用的语法),我有两个问题。如果您能为我提供这些问题的答案,我将永远感激不尽。

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);
}

我已经证实这确实是唯一正确的语法(我很高兴发现我弄错了)。我的问题更多是关于语法背后的推理,如问题正文中所述。

谢谢你的帮助。

4

3 回答 3

5

为什么不是语法...

为什么语法不能...

你希望我们说什么?决定这种语法的人(主要是 Stroustrup 本人,AFAIK)认为他们的语法比你的更好。

哪个更好或更容易记住我不知道 - 但我确实发现他们的比你的更有意义。当然,你可以不同意。

编辑:好的,亚历山大很好地回答了问题 #2。关于#1:

不同之处在于A<S>命名一个type,这是函数参数所期望的,而A它本身是一个template的名称,从中创建类型,如果您想与模板而不是类型成为朋友,这是有道理的:

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

可以与特定的模板实例而不是整个模板成为朋友,但为此模板必须在声明时编译器已经知道(即声明)friend

template< typename T >
class foo;

class bar {
  friend class foo<int>; // foo<int> being the name of a specific instance of foo
};

因此,与模板成为朋友是一个例外(“通常”的friend声明声明了一个函数或类)并且确实需要不同的语法。

于 2010-01-31T19:28:18.087 回答
5

虽然我不能说为什么选择这种语法,但我只能说我会支持语言设计者做出的这两个决定——它们对我来说很有意义。在问题 2 中,您不仅有一个模板,还有两个嵌套的模板级别。为什么定义模板类的模板成员的语法应该隐藏这个事实?这样,它只是对现有模板语法的重新组合,而您的需要特殊规则才能将嵌套模板的模板参数合并到一个template<>.

于 2010-01-31T19:33:33.177 回答
2

假设您的嵌套模板声明稍微复杂一点:

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);
}

突然之间,您的建议似乎不再那么合理或明显了。哪一组参数先出现?假设您使用 C++0x 并且有一个变量参数列表?

至于朋友声明,通过使用您建议的语法 ( friend struct <S, J>),您突然让编译器不得不推断出它S并且J意味着作为模板参数,不应该从某个随机范围内获取。假设有人引入了Sstruct A? S声明friend struct A<S,J>指的是哪个?编译器怎么知道?在外部范围中引入名称以从根本上改变嵌套范围中声明的含义是否合理?

如果你的意思是声明应该读为,total: template <typename S, int J> friend struct A<S, J>,那么为什么朋友转发模板声明看起来与标准模板声明有什么不同呢?当您已经在template <typename S, int J>部分中提到它们时,在模板名称之后包含模板变量的名称是多余的。

我还认为您建议的语法会使弄清楚模板专业化正在做什么变得更加困难,因为您必须查看代码的更多部分并关联事物。

例如,我的版本的模板特化A如下所示:

template <typename T>
struct A<T, 5> {
};

正如您所看到的,这与您建议的朋友前向声明语法非常接近,并且可能很难判断您是否打算指定一个专门的版本。它需要将模板参数与模板参数匹配,而按照目前的方式,如果你没有模板参数,你就不是在谈论专业化。

于 2010-01-31T19:49:34.197 回答