我正在研究在命名空间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);
^
你可以帮我弄清楚问题是什么吗?提前谢谢。
答案 0 :(得分:1)
重要的是要理解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的错误。
答案 1 :(得分:1)
经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 中的错误可能可能是正确的。我不是语言专家。此外,我不想在此就此发表争论;这不是这个问题或答案的目的。
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)
中解决