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

时间:2012-04-29 20:18:13

标签: 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);
}

无法编译并显示错误消息

  

foo.cpp:18:5:错误:“operator==”中的“p == nullptr”不匹配   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)没有' t编译)。此外,这可以通过在类中定义函数来解决。

但是,出于统一的原因(我还需要其他朋友函数来定义外部)我想在类之外定义这个函数。

是否有一个“技巧”来使函数的类定义外部工作?

2 个答案:

答案 0 :(得分:5)

您有三种可能的选择

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

for ex

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

for ex

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

for ex

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

答案 1 :(得分:3)

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

如果在模板类中声明了友元函数,请执行以下操作:

template <typename T>
struct A {
  friend void f(A);
};

那么你所说的是任何一个名为f的非模板函数,它将A作为参数将成为A的朋友。所以你需要分别定义这些函数:

inline void f(A<int>) {...}
inline void f(A<float>) {...}
// etc.

虽然在课堂内定义它是一种捷径。

在这种情况下,没有办法为每个T创建一个定义朋友f(A)的模板,因为你已经声明它是非模板函数是朋友。事实上,它是一个非模板函数,使其可用于您的示例,因为当编译器查找匹配函数时,非模板函数允许比模板函数更多的转换。

有一个相当一般的解决方法,虽然它有点凌乱。你可以定义其他模板函数来处理你的nullptr,或者你可能抛出的任何其他函数,只需将它推迟到你的main函数:

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::nullptr_t lhs,foo<T> rhs)
{
  return foo<T>(lhs)==rhs;
}

另外,即使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);
}