一个涉及非平凡模板和朋友声明的C ++语法问题

时间:2010-01-31 19:08:40

标签: c++ templates syntax friend

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

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

我已经确认这确实是唯一正确的语法(我很乐意发现我错了)。我的问题更多地是关于语法背后的原因,正如问题的主体所解释的那样。

感谢您的帮助。

3 个答案:

答案 0 :(得分:5)

  

为什么不是语法...

     

为什么语法不能......

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

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

修改 确定,Alexander has nicely answered question #2。关于#1:

区别在于A<S>命名类型,这是函数参数的预期值,而A本身就是模板的名称,从哪个类型创建,如果你想与模板而不是类型成为朋友,这是有意义的:

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

可以与特定的模板实例交配,而不是整个模板,但为此,模板必须已经被编译器知道(即声明){ {1}}声明:

friend

所以交朋友模板是一个例外(“通常的”template< typename T > class foo; class bar { friend class foo<int>; // foo<int> being the name of a specific instance of foo }; 声明声明一个函数或类)并且确实需要不同的语法。

答案 1 :(得分:5)

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

答案 2 :(得分: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>),你突然让编译器必须推断SJ是模板参数,应该不要从一些随机范围中抓取。假设有人在与S相同的范围级别引入了类型struct A?声明S引用哪个friend struct A<S,J>?编译器如何知道?在外部作用域中引入名称以从根本上改变嵌套作用域中声明的含义是否合理?

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

我还认为你的建议语法会弄清楚模板专业化做了哪些更难的事情,因为你必须查看代码的更多部分并将事情关联起来。

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

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

正如您所看到的,这与您建议的朋友转发声明语法非常接近,并且可能很难判断您是否打算指定专门版本。它需要将模板参数与模板参数进行匹配,而对于当前完成的方式,如果没有模板参数,则不是在讨论专业化。