对于在类外定义的友元函数,模板上的隐式转换查找失败

Kon*_*lph 9 c++ templates c++11

以下代码

#include <cassert>
#include <cstddef>

template <typename T>
struct foo {
    foo(std::nullptr_t) { }
    //friend bool operator ==(foo lhs, foo rhs) { return true; }

    template <typename U>
    friend bool operator ==(foo<U> lhs, foo<U> rhs);
};

template <typename T>
inline bool operator ==(foo<T> lhs, foo<T> rhs) { return true; }

int main() {
    foo<int> p = nullptr;
    assert(p == nullptr);
}
Run Code Online (Sandbox Code Playgroud)

无法使用错误消息进行编译

foo.cpp:18:5:错误:operator==' p == nullptr'
in''foo.cpp 不匹配:18:5:注意:候选人是:
foo.cpp:14:13:注意:template<class T> bool operator==(foo<T>, foo<T>)
foo.cpp:14:13:注意:模板参数推导/替换失败:
foo.cpp:18:5:注意:不匹配的类型' foo<T>'和' std::nullptr_t'

但是,如果我使用类中的定义,代码将按预期工作.

让我说我理解错误消息:模板参数T不能推断为类型nullptr(顺便说一句,decltype(*nullptr)不编译).此外,这可以通过定义类中的函数来解决.

但是,出于统一的原因(我需要在外面定义其他友元函数),我想在类之外定义这个函数.

是否有一个"技巧"使函数的类外定义工作?

Abh*_*jit 5

有三种可能的选择

  • 声明并定义一个rhs类型的新朋友函数 std::nullptt_t

对于前

inline bool operator ==(foo<T> lhs, std::nullptr_t rhs) { return true; }
Run Code Online (Sandbox Code Playgroud)
  • 或者在将变量等同时nullptr,明确说明类型nullptr

对于前

assert(p == foo<int>(nullptr));
Run Code Online (Sandbox Code Playgroud)
  • 声明并定义一个rhs类型的新朋友函数 void *

对于前

inline bool operator ==(foo<T> lhs, void *rhs) {         
    if (rhs == nullptr) 
        return true; 
    else
        return false;
    }
Run Code Online (Sandbox Code Playgroud)


Vau*_*ato 3

Abhijit 已经给了你基本的解决方案,但我想我应该稍微解释一下,因为这是一个有趣的问题。

如果您在模板类中声明一个友元函数,如下所示:

template <typename T>
struct A {
  friend void f(A);
};
Run Code Online (Sandbox Code Playgroud)

那么你的意思是,任何以 A 作为参数的名为 f 的非模板函数都将是 A 的友元。因此,你需要单独定义这些函数:

inline void f(A<int>) {...}
inline void f(A<float>) {...}
// etc.
Run Code Online (Sandbox Code Playgroud)

尽管在类中定义它是一个快捷方式。

在这种情况下,无法制作一个为每个 T 定义友元 f(A) 的模板,因为您已经声明非模板函数才是友元。事实上,它是一个非模板函数,因此可以在您的示例中使用,因为当编译器查找匹配函数时,非模板函数比模板函数允许更多转换。

有一个相当通用的解决方法,尽管它有点混乱。您可以定义其他模板函数来处理您的 nullptr 或您可能抛出的任何其他内容,并将其推迟到您的 main 函数:

template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
  return lhs==foo<T>(rhs);
}
Run Code Online (Sandbox Code Playgroud)

为了对称,你可能想用两种方法来做:

template <typename T>
inline bool operator ==(std::nullptr_t lhs,foo<T> rhs)
{
  return foo<T>(lhs)==rhs;
}
Run Code Online (Sandbox Code Playgroud)

另外,即使 U 和 T 不是同一类型,您定义友元函数的方式也会成为operator==(foo<U>,foo<U>)友元。foo<T>这在实践中可能不会产生太大的影响,但有一种技术上更好的方法可以做到这一点。它涉及前向声明模板函数,然后使模板参数的特化成为朋友。

这是一个完整的示例:

template <typename> struct foo;

template <typename T>
inline bool operator==(foo<T> lhs,foo<T> rhs);

template <typename T>
struct foo {
    foo(std::nullptr_t) { }

    friend bool operator==<>(foo lhs,foo rhs);
};

template <typename T>
inline bool operator ==(foo<T> lhs,foo<T> rhs)
{
  return true;
}

template <typename T>
inline bool operator ==(foo<T> lhs, std::nullptr_t rhs)
{
  return lhs==foo<T>(rhs);
}

template <typename T>
inline bool operator ==(std::null_ptr_t lhs,foo<T> rhs)
{
  return foo<T>(lhs)==rhs;
}

int main() {
    foo<int> p = nullptr;
    assert(p == nullptr);
    assert(nullptr == p);
    assert(p == p);
}
Run Code Online (Sandbox Code Playgroud)