Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to provide the definition of a friend operator in a nested class of a template class?

Tags:

c++

This works:

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

This does not work:

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;
}

When I try to use it in a map for example, I get linker errors:

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[{}];
}

How can I define this operator correctly outside the class?

like image 248
Neil Kirk Avatar asked Oct 10 '15 23:10

Neil Kirk


People also ask

Can we declare a template function as the friend of the class?

Templates themselves can't be friends, though the closest to what this could mean is to make all specializations of one template friends of all specializations of another template (then friend declaration has to be templated - see the accepted answer).

Where to put friend class declaration?

Friends aren't in the class's scope, and they aren't called using the member-selection operators (. and ->) unless they're members of another class. A friend function is declared by the class that is granting access. The friend declaration can be placed anywhere in the class declaration.

What does template typename t mean?

template <typename T> ... This means exactly the same thing as the previous instance. The typename and class keywords can be used interchangeably to state that a template parameter is a type variable (as opposed to a non-type template parameter).

How is a template class different from a template function?

For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.


1 Answers

You would normally do such a thing by forward-declaring the template class and the friend function and then providing a specialization within the class definition. However, in this case it is not as easy - having a dependent type puts the Tim class in a non-deduced context, so the deduction will fail. However, there's a way around it:

#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;

}

Basically, what I've done here is let the compiler deduce the full Bob<Tim>::Dave as the template parameter for operator<. However, clearly a simple definition would allow for any types to be deduced for T potentially leading to some difficult-to-understand problems. To avoid it I added a small tag class DaveTag which allows to prevent instantiations of our very generic operator< for anything but Dave.

like image 55
Rostislav Avatar answered Sep 30 '22 21:09

Rostislav