为什么在NVCC中,定义隐藏友元二元运算符operator- 与operator== 的顺序,以及使用int与 concepts的区别,会对NVCC产生影响?
我正在为CUDA设备指针创建一个类似指针的类型包装器,但在常规的MSVC或 GCC编译器中遇到了一个问题,无法在其他编译器上重现。基本上,取决于我定义运算符- 的顺序,我把它定义为一个隐藏的友元函数,如下所示:
[[nodiscard]]
friend TestPointerLike operator-(TestPointerLike lhs, std::integral auto n) noexcept {
// lhs -= n; removed for now for simplicity in example, since only testing compilation
return lhs;
}
根据是在 operator == 之前用 std::nullopt_t 定义,还是之后,或者如果我使用 int,或者为 n 使用 std::integral auto,并且我把它设为非隐藏友元并仅在类定义之外定义它,它可能会编译也可能不会编译 == 运算符。为什么会这样?
以下是最小示例(总共有四个测试,最后一个测试 TestPointerLike4 是失败的情况)。
Godbolt链接也在这里(使用NVCC):https://godbolt.org/z/cWsP3zoEj Godbolt链接到它在MSVC和 GCC中工作的版本:https://godbolt.org/z/a7EKr5hb9
#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;
}
}
我得到的错误是:
<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
解决方案
In TestPointerLike4, the compiler may fail to resolve operator== because the hidden operator- declaration interferes with ADL or overload resolution when std::nullopt_t is involved.
NVCC may not consider the operator== overload as viable if the operator- is defined in a way that confuses the lookup order or template deduction.
Try to replace std::nullopt_t with std::nullptr_t or a plain int temporarily to isolate the issue.