3

Thanks to Daniel Frey's answer to this post, I know how to declare a template friend function to a template class with the same template parameters. Unfortunately, the syntax for declaring a friend function with additional template parameters still escapes me. I would like to achieve something like this:

template <typename T>
class Obj;

template <typename T>
Obj<T> make_obj(T t);

template <typename T, typename RetVal>
RetVal ret_obj(T t);

template <typename T>
class Obj {
private:
    T & t;
    Obj (T & t) : t(t) { }
    Obj() = delete;

    friend Obj make_obj<T>(T t);

    template <typename RetVal>
        friend RetVal ret_obj<T, RetVal>(T t);
};

template <typename T>
Obj<T> make_obj(T t) { 
    return Obj<T>(t);
}

template <typename T, typename RetVal>
RetVal ret_obj(T t) {
    return RetVal(make_obj(t).t);
}

I know that the same question has already been asked in this post, but the accepted answer there does not seem to be what I want: changing the parameter name to T2 makes the function a friend of all specializations of the object, while I want to keep T the same as in the class.

Community
  • 1
  • 1
David Nemeskey
  • 640
  • 1
  • 5
  • 16

1 Answers1

2

It is impossible to let friend declarations refer to partial specializations - either they refer to a specific specialization or to the primary template. Moreover, function templates cannot be partially specialized anyway.
What is not possible with function templates is often doable using class templates though:

template <typename T>
struct ret_obj_helper {
    // Here goes the original definition of ret_obj - the important difference
    // is the location of the template parameter T, which is the one
    // fixed by the friend declaration below
    template <typename RetVal>
    RetVal ret_obj(T t) {return RetVal(make_obj(t).t);}
};

// I guess RetVal, having to be explicitly specified, better goes first (?)
template <typename RetVal, typename T>
RetVal ret_obj(T&& t)
{
    // Overcomplicated for the sake of perfect forwarding
    return ret_obj_helper<typename std::remove_reference<T>::type>{}.
      template ret_obj<RetVal>(std::forward<T>(t));
}

template <typename T>
class Obj {
private:
    T t;
    Obj (T t) : t(t) { }
    Obj() = delete;

    friend Obj make_obj<T>(T t);

    // Make all specializations of the member function template 
    // of ret_obj_helper<T> a friend, regardless of the return type
    template <typename RetVal>
    friend RetVal ret_obj_helper<T>::ret_obj(T t);
};

Demo.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Yeah, I knew it will boil down to this after reading the comments above. Just a question though: if the reference is not removed, calling the function with one would not compile? – David Nemeskey Jan 28 '15 at 11:11
  • What is the benefit of using a public struct, instead of a public static member? I can see no difference in the degree of encapsulation. Since the public function cannot be a friend, it has to go through an ugly public interface. – user877329 Nov 30 '16 at 10:10