如何与模板类的构造函数成为朋友?

时间:2010-05-12 23:25:53

标签: c++ templates gcc constructor friend

为什么

class A;
template<typename T> class B
{
private: 
    A* a;

public:  
    B();
};


class A : public B<int>
{
private:    
    friend B<int>::B<int>();
    int x;
};


template<typename T>
B<T>::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

结果

  

../ src / main.cpp:15:错误:无效使用构造函数作为模板
  ../src/main.cpp:15:注意:使用'B :: B'而不是'B :: class B'来命名限定名称中的构造函数

friend B<int>::B<int>()更改为friend B<int>::B()会导致

  

../ src / main.cpp:15:错误:在'B'类中声明的'无效B :: B()'成员函数

完全删除模板时

class A;
class B
{
private:
    A* a;

public:
    B();
};


class A : public B
{
private:
    friend B::B();
    int x;
};


B::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

编译并执行得很好 - 尽管我的IDE说朋友B :: B()语法无效?

4 个答案:

答案 0 :(得分:5)

根据CWG defect 147的分辨率(分辨率已合并到C ++ 03中),命名类模板特化的非模板构造函数的正确方法是:

B<int>::B();

而不是

B<int>::B<int>();

如果允许后者,当你有一个类模板特化的构造函数模板专门化时会有一个歧义:第二个<int>是否适用于类模板或构造函数模板? (有关详细信息,请参阅上面链接的缺陷报告)

因此,将类模板特化的构造函数声明为朋友的正确方法是:

friend B<int>::B();

Comeau 4.3.10.1和Intel C ++ 11.1都接受这种形式。 Visual C ++ 2008和Visual C ++ 2010都不接受该表单,但都接受(错误的)表单friend B<int>::B<int>();(我将在Microsoft Connect上提交缺陷报告)。

gcc不接受4.5版之前的任何一种形式。针对gcc 3.0.2报告了Bug 5023,但错误报告中请求的解决方案是无效表单。似乎bug 9050的解析也解决了这个问题,gcc 4.5接受了正确的形式。 Georg Fritzsche在对该问题的评论中证实了这一点。

答案 1 :(得分:1)

您的IDE在后一种情况下将朋友B :: B()显示为无效语法的原因是什么? IDE错误。

如果你无法升级,我在gcc中找到了一个模板案例的解决方法是将B()的实现移动到一个成员函数,void B :: init(),并为此授予友谊。我敢打赌,这也会关闭你的IDE。

即使你要编译它,但是一旦你尝试实例化B,你就会遇到堆栈溢出问题。

答案 2 :(得分:1)

typedef不帮助吗? e.g。

class A : public B<int>
{
    typedef B<int> Base;   
    friend Base::Base();
    int x;
};

编辑:C ++ 0x的最终委员会草案在3.4.3.1节中包含以下语言[ class.qual ]:

  

在查找中,构造函数是可接受的查找结果,而嵌套名称说明符指定类 C :如果在之后指定的名称当在 C 中查找时,嵌套名称说明符 C 的注入类名(第9条),或者如果之后指定的名称嵌套名称说明符标识符 simple-template-id 模板名称相同 nested-name-specifier 的最后一个组件中,该名称被认为是为 C 类的构造函数命名。

听起来像嵌套名称说明符Base)与标识符中指定的名称(Base::)相同嵌套名称说明符的最后一个组件,因此此代码确实命名了一个构造函数。

但我无法将其与第12.1节[ class.ctor ]协调一致:

  

因为构造函数没有名称,所以在名称查找期间永远找不到它们

哦,真的吗? 3.4.3.1中的语言又是如何工作的?

  

typedef-name 不得用作构造函数声明的 declarator-id 中的 class-name

这似乎很清楚,除了第12.1节似乎只讨论构造函数的引入声明,因为第1段排除了嵌套名称说明符friend和{{1 }}。如果它确实适用于朋友声明,它似乎也禁止using

  

使用 function-specifiers (7.1.2)的可选序列,后跟构造函数的类名后跟参数列表的特殊声明符语法用于声明或定义构造函数。

那些函数说明符friend Base::B();inlinevirtual,而构造函数无论如何都不能是explicit

答案 3 :(得分:0)

我的猜测;在这里与朋友模板化的构造者进入怪癖领域。这在VS2010上编译并运行正常,但是当A的默认构造函数调用B的默认构造函数然后再次实例化A时,它会产生堆栈溢出。