NVCC中隐藏友元运算符定义顺序、类型选择为何影响编译?
NVCC专属编译问题:指针包装类中隐藏友元运算符的兼容性问题
我在为CUDA设备指针实现类指针式包装类型时,碰到了仅NVCC编译器会触发、MSVC和GCC完全正常的编译问题。具体表现为:
- 隐藏友元
operator-的定义顺序(在operator==(std::nullptr_t)之前还是之后) - 参数
n的类型选择(用int还是std::integral auto) operator-是定义为类内隐藏友元还是类外部的非隐藏友元
以上三点都会直接影响operator==(std::nullptr_t)的编译是否通过。
最小复现代码
#include <type_traits> #include <concepts> #include <iostream> template<typename T> struct TestPointerLike { private: T *m_data = nullptr; public: [[nodiscard]] T *get() noexcept { return m_data; } [[nodiscard]] const T *get() const { return m_data; } TestPointerLike() noexcept = default; explicit constexpr TestPointerLike(std::nullptr_t) noexcept : TestPointerLike() {}; [[nodiscard]] friend bool operator==(TestPointerLike dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() == nullptr; } [[nodiscard]] friend bool operator!=(TestPointerLike dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() != nullptr; } [[nodiscard]] friend TestPointerLike operator-(TestPointerLike lhs, std::integral auto n) noexcept { // lhs -= n; return lhs; } }; template<typename T> struct TestPointerLike2 { private: T *m_data = nullptr; public: [[nodiscard]] T *get() noexcept { return m_data; } [[nodiscard]] const T *get() const { return m_data; } TestPointerLike2() noexcept = default; explicit constexpr TestPointerLike2(std::nullptr_t) noexcept : TestPointerLike2() {}; [[nodiscard]] friend TestPointerLike2 operator-(TestPointerLike2 lhs, int n) noexcept { // lhs -= n; return lhs; } [[nodiscard]] friend bool operator==(TestPointerLike2 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() == nullptr; } [[nodiscard]] friend bool operator!=(TestPointerLike2 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() != nullptr; } }; template<typename T> struct TestPointerLike3 { private: T *m_data = nullptr; public: [[nodiscard]] T *get() noexcept { return m_data; } [[nodiscard]] const T *get() const { return m_data; } TestPointerLike3() noexcept = default; explicit constexpr TestPointerLike3(std::nullptr_t) noexcept : TestPointerLike3() {}; friend TestPointerLike3 operator-(TestPointerLike3 lhs, std::integral auto n) noexcept; [[nodiscard]] friend bool operator==(TestPointerLike3 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() == nullptr; } [[nodiscard]] friend bool operator!=(TestPointerLike3 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() != nullptr; } }; template<typename T> [[nodiscard]] TestPointerLike3<T> operator-(TestPointerLike3<T> lhs, std::integral auto n) noexcept { // lhs -= n; return lhs; } template<typename T> struct TestPointerLike4 { private: T *m_data = nullptr; public: [[nodiscard]] T *get() noexcept { return m_data; } [[nodiscard]] const T *get() const { return m_data; } TestPointerLike4() noexcept = default; explicit constexpr TestPointerLike4(std::nullptr_t) noexcept : TestPointerLike4() {}; [[nodiscard]] friend TestPointerLike4 operator-(TestPointerLike4 lhs, std::integral auto n) noexcept { // lhs -= n; return lhs; } [[nodiscard]] friend bool operator==(TestPointerLike4 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() == nullptr; } [[nodiscard]] friend bool operator!=(TestPointerLike4 dev_ptr, std::nullptr_t) noexcept{ return dev_ptr.get() != nullptr; } }; int main() { { TestPointerLike<double> f64_ptr; bool bool_test = f64_ptr == nullptr; } { TestPointerLike2<double> f64_ptr; bool bool_test = f64_ptr == nullptr; } { TestPointerLike3<double> f64_ptr; bool bool_test = f64_ptr == nullptr; } { TestPointerLike4<double> f64_ptr; bool bool_test = f64_ptr == nullptr; } }
编译错误信息(NVCC下)
<source>(187): error: no operator "==" matches these operands operand types are: TestPointerLike4<double> == std::nullptr_t bool bool_test = f64_ptr == nullptr; ^ <source>(187): note #3328-D: built-in operator==(<promoted arithmetic>, <promoted arithmetic>) does not match because argument #1 does not match parameter bool bool_test = f64_ptr == nullptr; ^ <source>(187): note #3328-D: built-in operator==(<nullptr>, <nullptr>) does not match because argument #1 does not match parameter bool bool_test = f64_ptr == nullptr; ^ 1 error detected in the compilation of "<source>". Compiler returned: 2
测试用例差异分析
- TestPointerLike:编译正常。
operator==(std::nullptr_t)定义在operator-之前,operator-使用std::integral auto参数。 - TestPointerLike2:编译正常。
operator-使用int参数且定义在operator==之前。 - TestPointerLike3:编译正常。
operator-采用类内声明、类外定义的友元形式,参数为std::integral auto。 - TestPointerLike4:编译失败。
operator-使用std::integral auto参数且定义在operator==之前。
临时解决方案
针对NVCC的这一兼容性问题,可采用以下任一方式规避:
- 调整运算符定义顺序,将
operator==(std::nullptr_t)放在operator-之前; - 将
operator-的std::integral auto参数替换为具体的整数类型(如int); - 将
operator-改为类内声明、类外定义的友元形式。
内容的提问来源于stack exchange,提问作者Krupip
相关产品推荐
相关产品推荐

