CUDA C++ 中的友元函数

friend function in CUDA C++

我正在处理 class A 和 B 在命名空间 test0 中声明的问题,以及 class A 在命名空间 test1 中声明的友元函数 f。函数 f 接收一个 class 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++ pass 剥离。我不知道 为什么 它被删除了,但这是错误的来源。

如果您的应用程序确实存在问题,我建议您将此作为错误报告给 NVIDIA。

经过 NVIDIA 开发团队的审查,这似乎暴露了 gnu 编译器中的错误。 nvcc 工具链的前端处理确实创建了一个主机代码(传递给主机编译器),它删除了 b 类型的名称空间限定,但这应该是可以接受的,因为 B 已经在 test0 命名空间中声明。

这似乎对 gnu 社区有 already been reported

作为支持数据点,Fedora 25 上的 clang++ 3.9.1 编译了@talonmies 给出的 中报告的代码,没有错误也没有警告。在我通过 Fedora25 上的 gnu 6.4.1 进行测试时,gnu 工具链仍然会抛出错误。我并不是声称这是一个证据点,只是建议 gnu 中的错误声明可能 可能是正确的。我不是语言专家。此外,我不想在这里就此展开争论;这不是这个问题或答案的目的。

NVIDIA 开发团队已对该问题进行审核,并希望在未来的 CUDA 版本中提供修复或解决方法。

同时,建议的源代码级解决方法是在 class A 中为 B 使用虚拟 typedef。即:

class A {
    typedef B dummy_t;
    friend void test1::f(dummy_t & b);
};

更新:

该问题应在 CUDA 10.1.105 (CUDA 10.1) 中解决