6

为什么

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'中声明的'void B::B()'成员函数</p>

同时完全删除模板

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

4 回答 4

5

根据CWG 缺陷 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 报告了错误 5023,但错误报告中请求的解决方案是无效的形式。看来bug 9050的解决方案也解决了这个问题,并且 gcc 4.5 接受了正确的形式。Georg Fritzsche 在对该问题的评论中证实了这一点。

于 2010-05-13T01:52:43.473 回答
1

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

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

但是,即使您可以编译它,一旦尝试实例化 B,您也会遇到堆栈溢出问题。

于 2010-05-13T02:06:50.007 回答
1

typedef 没有帮助吗?例如

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 条),或者如果在 nested-name-specifier 之后指定的名称与在nested-name-specifier最后一个组件中的标识符simple-template-id模板名称相同,则该名称相反,它被认为是命名类C的构造函数。

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

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

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

哦真的吗?3.4.3.1 中的语言如何再次工作?

typedef-name不应用作构造函数声明的declarator-id中的类名。

这似乎很清楚,除了第 12.1 节似乎只讨论了构造函数的引入声明,因为第 1 段不包括嵌套名称说明符friendusing。如果它确实适用于朋友声明,它似乎也禁止friend Base::B();

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

那些函数说明符inline, virtual,和构造函数无论如何explicit都不能。virtual

于 2010-05-13T02:08:44.130 回答
0

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

于 2010-05-13T00:51:54.297 回答