C++ Logo

std-discussion

Advanced search

Re: Out-of-class definition of friend function template of class template possible?

From: Yongwei Wu <wuyongwei_at_[hidden]>
Date: Sun, 24 Apr 2022 22:00:45 +0800
On Sun, 24 Apr 2022 at 20:07, Bo Persson via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> On 2022-04-24 at 11:07, Yongwei Wu via Std-Discussion wrote:
> > For code like the following:
> >
> > template <typename T>
> > class Wrapper {
> > public:
> > Wrapper(T value) : value_(value) {}
> >
> > template <typename CharT, typename Traits>
> > friend std::basic_ostream<CharT, Traits>&
> > operator<<(std::basic_ostream<CharT, Traits>& os,
> > const Wrapper& obj)
> > {
> > os << obj.value_;
> > return os;
> > }
> >
> > private:
> > T value_;
> > };
> >
> > Is there a way to move the operator<< definition outside the class
> template?
> >
> > I have not found a direction solution, though some workarounds are
> > possible: say, proxying through a member function template; or make the
> > operator<< template have three template parameters:
> >
> > template <typename CharT, typename Traits, typename U>
> > friend std::basic_ostream<CharT, Traits>&
> > operator<<(std::basic_ostream<CharT, Traits>& os,
> > const Wrapper<U>& obj);
>
> That's not really an assymetry, but the correct way to do it.
>
> Inside that class, Wrapper is a shorthand for Wrapper<T>. Outside the
> class there is no shorthand available, so you need to specify its
> template parameter explicitly.
>
> And then you have to make the friend declaration match the out-of-class
> definition (with an equal number of template parameters).
>

Arguable somehow. The three-parameter solution makes operator<< for
Wrapper<char> a friend of Wrapper<int>, for example. The inline solution is
stricter about who is the friend. Of course, it is relatively trivial.

We can specify correctly if T is the only template parameter for operator<<,
say, if we only support std::ostream (but not the wide-character and other
character types).

This works:

template <typename T>
class Wrapper;

template <typename T>
std::ostream& operator<<(std::ostream& os, const Wrapper<T>& obj);

template <typename T>
class Wrapper {
public:
    …

    friend std::ostream&
    operator<< <T>(std::ostream& os,
                   const Wrapper& obj);
};

This does not (the compiler will complain about partial specialization):

template <typename T>
class Wrapper;

template <typename CharT, typename Traits, typename T>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const Wrapper<T>& obj);

template <typename T>
class Wrapper {
public:
    …

    template <typename CharT, typename Traits>
    friend std::basic_ostream<CharT, Traits>&
    operator<< <CharT, Traits, T>(std::basic_ostream<CharT, Traits>& os,
                                  const Wrapper& obj);
};

-- 
Yongwei Wu
URL: http://wyw.dcweb.cn/

Received on 2022-04-24 14:00:43