我正在研究在命名空间test0中声明类A和B以及在命名空间test1中声明的类A的友元函数f的问题。函数f接收B类对象的引用作为参数。这是一个简化的例子。
namespace test0 {
class B;
}
namespace test1 {
void f(test0::B& b);
}
namespace test0 {
class A {
friend void test1::f(test0::B& b);
};
}
该代码适用于g ++。但是nvcc给出了以下编译错误。
a.cu:11:22: error: ‘B’ has not been declared
friend void test1::f(test0::B& b);
^
a.cu:11:27: error: ‘void test1::f(int&)’ should have been declared inside ‘test1’
friend void test1::f(test0::B& b);
^
你能帮我弄清楚问题是什么吗?先感谢您。
重要的是要理解nvcc不是编译器,它是编译器驱动程序,在这两种情况下,代码都是用gcc编译的,错误是gcc生成的错误。如果您将该代码放在.cc
扩展文件中并通过nvcc进行编译,则不会出现错误。
但是在编译CUDA代码时(在这种情况下在.cu
文件中),在代码和编译它的最终g ++传递之间有一些中间处理阶段。在幕后,正在发生的事情是CUDA C ++前端解析器将您的代码转换为:
# 1
# 2
namespace test0 {
# 3
class B;
# 4
}
# 6
namespace test1 {
# 7
void f(test0::B & b);
# 8
}
# 10
namespace test0 {
# 11
class A {
# 12
friend void test1::f(B & b);
# 13
};
# 14
}
将其与原始friend void test1::f(test0::B& b);
进行比较,您可以看到命名空间已被cudafe ++传递剥离。我不知道为什么它被剥离了,但那是错误的根源。
如果它是您应用程序中的真正问题,我建议将此报告为NVIDIA的错误。
经过NVIDIA开发团队的审核后,似乎可能会暴露gnu编译器中的错误。确实,nvcc
工具链的前端处理创建了一个主机代码(传递给主机编译器),它删除了b
类型的命名空间限定,但这应该是可以接受的,因为B
已经在test0
命名空间中声明了。
看来这对gnu社区有already been reported。
作为支持数据点,Fedora 25上的clang ++ 3.9.1编译了由@talonmies给出的answer中报告的代码,没有错误也没有警告。在我通过Fedora25上的gnu 6.4.1进行测试时,gnu工具链仍会抛出错误。我并没有声称这是一个证明点,只是暗示gnu中的bug声称可能是正确的。我不是语言专家。此外,我不想在此就此发表争论;这不是这个问题或答案的目的。
NVIDIA开发团队已经对该问题进行了审核,并希望在未来的CUDA版本中进行修复或解决。
与此同时,建议的源级解决方法是在类B
中为A
使用虚拟typedef。即:
class A {
typedef B dummy_t;
friend void test1::f(dummy_t & b);
};
更新:
该问题应在CUDA 10.1.105(CUDA 10.1)中解决