Home > Net >  Make a class befriend a family of functions
Make a class befriend a family of functions

Time:11-09

I have a class template

template <class Key, class T, class Hash, template <class> class Allocator>
class Table; 

and a function template

template <class Key, class T, class DevHash, template <class> class DevAllocator, class HostHash, template <class> class HostAllocator>
void copyTableToHost(const Table<Key, T, DevHash, DevAllocator> &table, Table<Key, T, HostHash, HostAllocator> &hostTable);

Now I want to grant copyTableToHost() access to private members of both table and hostTable. In order to do that I made a friend declaration in Table class:

template <class DevHash, template <class> class DevAllocator, class HostHash, template <class> class HostAllocator>
friend void copyTableToHost<Key, T, DevHash, DevAllocator, HostHash, HostAllocator>(const Table<Key, T, DevHash, DevAllocator> &table, Table<Key, T, HostHash, HostAllocator> &hostTable);

My reasoning is that I don't need to specify Key and T as template parameters here since these are fixed for a given specialization. At the same time a given specialization needs to be friends with a whole class of functions which differ by the choice of DevHash, DevAllocator, HostHash and HostAllocator (I am not sure if template template parameters don't mess things up here ...).

The errors I get are of the form member Table<Key, T, Hash, Allocator>::[member name] is inaccessible which makes me believe that the friend declaration didn't work as intended.

CodePudding user response:

If you want it as a free friend function template, one option could be to befriend all instances of it:

template <class Key, class T, class Hash, template <class> class Allocator>
class Table {
    template <class K, class Ty, 
              class Hash1, template <class> class Allocator1,
              class Hash2, template <class> class Allocator2>
    friend void copyTableToHost(const Table<K, Ty, Hash1, Allocator1>& table,
                                      Table<K, Ty, Hash2, Allocator2>& hostTable);

    int x = 0; // private
};

template <class Key, class T,
          class Hash1, template <class> class Allocator1,
          class Hash2, template <class> class Allocator2>
void copyTableToHost(const Table<Key, T, Hash1, Allocator1>& table,
                           Table<Key, T, Hash2, Allocator2>& hostTable)
{
    hostTable.x = table.x; // now ok
}

Another option could be to instead use iterators and populate the table in a member function that takes iterators with the requirement they are dereferenced into a std::pair<Key, T> - or whatever class template you are using to package each <Key, T>. It could look like this:

#include <type_traits>
#include <iterator>

template <class Key, class T, class Hash, template <class> class Allocator>
class Table {
public:
    template<class It>
    requires std::is_same_v<typename std::iterator_traits<It>::value_type,
                            std::pair<Key, T>>
    void copyFrom(It first, It last) {
        // copy from `first` to `last`
    }
};
  • Related