如何在模板类的嵌套类中提供友元运算符的定义?

时间:2015-10-10 23:12:44

标签: c++

这有效:

template<class Tim>
struct Bob
{
    struct Dave
    {
        Tim t{};
        friend bool operator < (const Dave& a, const Dave& b)
        {
            return a.t < b.t;
        }
    } d;
};

这不起作用:

template<class Tim>
struct Bob
{
    struct Dave
    {
        Tim t{};
        friend bool operator < (const Dave& a, const Dave& b);
    } d;
};

template<class Tim>
bool operator < (const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b)
{
    return a.t < b.t;
}

例如,当我尝试在地图中使用它时,我会收到链接器错误:

1>ConsoleApplication1.obj : error LNK2019: unresolved external symbol "bool __cdecl operator<(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)" (??M@YA_NABUDave@?$Bob@H@@0@Z) referenced in function "public: bool __thiscall std::less<struct Bob<int>::Dave>::operator()(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)const " (??R?$less@UDave@?$Bob@H@@@std@@QBE_NABUDave@?$Bob@H@@0@Z)

int main()
{
    std::map<Bob<int>::Dave, int> v;
    v[{}];
}

如何在类正确定义此运算符?

2 个答案:

答案 0 :(得分:4)

通常通过向前声明模板类和友元函数然后在类定义中提供特化来做这样的事情。但是,在这种情况下,它并不那么容易 - 具有依赖类型将Tim类放在非推导的上下文中,因此推论将失败。但是,有一种解决方法:

#include <iostream>
#include <type_traits>
#include <map>

template<class T>
struct Bob;

template<typename T, typename>
bool operator < (const T& a, const T& b);

struct DaveTag {};

template<class Tim>
struct Bob
{
    struct Dave : DaveTag
    {
        Tim t{};


        friend bool operator < <Bob<Tim>::Dave, void>(const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b);
    } d;
};

template<typename T, typename = typename std::enable_if<std::is_base_of<DaveTag, T>::value>::type>
bool operator < (const T& a, const T& b)
{
    return a.t < b.t;
}

struct X {
    double t;
};

int main()
{
    std::map<Bob<int>::Dave, int> v;
    v[{}];

    // This won't work
    // X x, y;
    //bool b = x < y;

}

基本上,我在这里做的是让编译器推导出完整的Bob<Tim>::Dave作为operator<的模板参数。但是,显然一个简单的定义将允许推导出T的任何类型可能导致一些难以理解的问题。为了避免这种情况,我添加了一个小标记类DaveTag,它允许阻止我们非常通用的operator<实例化Dave以外的任何内容。

答案 1 :(得分:0)

这是一个令人满意的解决方法。我不想出于各种原因在类中实现运算符(此时没有定义,这里有一些显式模板实例化)但我可以使用以下内容:

struct Bob
{
    struct Dave
    {
        Tim t{};

        static bool LessThan(const Dave& a, const Dave& b);

        friend bool operator < (const Dave& a, const Dave& b)
        {
            return LessThan(a, b);
        }
    } d;
};

现在静态函数LessThan可以以正常方式在类外实现。