Home > Software engineering >  Wierd stuff happens when overloading operator<< with a class template
Wierd stuff happens when overloading operator<< with a class template

Time:11-15

Here's the functionality I am expecting to achieve:

darray<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
std::cout << a << std::endl; // displays: {1, 2, 3}

My implementation:

template <typename T>
class darray
{
private:
    long m_capacity;
    long m_size;
    T* m_data;
    void resize();

public:
    // constructors & destructors
    darray();

    // operations
    void push_back(T);
    std::ostream& print(std::ostream&) const;
    template<typename U> friend std::ostream& operator<<(std::ostream& os, U const& ar);
};

template<typename T>
std::ostream& darray<T>::print(std::ostream& os) const
{
    os << "{ ";
        for (size_t i = 0; i < m_size; i  )
        {
            os << m_data[i] << ", ";
            if ( i == m_size - 1 )
                os << m_data[i];
        }
        os << " }\n";
        return arr;
}

template<typename U>
std::ostream& operator<<(std::ostream& os, U const& obj)
{
    return obj.print(os);
}

produces an error:

error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘const char [66]’)

But, when I change the parameter of operator<< to accept a darray<U> instead , it works fine:

template<typename U>
std::ostream& operator<<(std::ostream& os, darray<U> const& obj)
{
    return obj.print(os);
}

What am I missing here?

Update:

I also tried doing this, changing the parameter to darray<U> type in the definition and the implementation, but it still produces the same error:

template <typename T>
class darray
{
private:
    long m_capacity;
    long m_size;
    T* m_data;
    void resize();

public:
    // constructors & destructors
    darray();

    // operations
    void push_back(T);
    std::ostream& print(std::ostream&) const;
    template<typename U> friend std::ostream& operator<<(std::ostream& os, darray<U> const& ar);
};

template<typename T>
std::ostream& darray<T>::print(std::ostream& os) const
{
    os << "{ ";
        for (size_t i = 0; i < m_size; i  )
        {
            os << m_data[i] << ", ";
            if ( i == m_size - 1 )
                os << m_data[i];
        }
        os << " }\n";
        return os;
}

template<typename U>
std::ostream& operator<<(std::ostream& os, darray<U> const& obj)
{
    return obj.print(os);
}

CodePudding user response:

Friend functions in template classes have to be defined inside the class declaration. This is the only way I have found to have the friend function to correctly accept an instance of the templated class with the expected template.

So here I would write:

...
friend std::ostream& operator<<(std::ostream& os, darray<T> const& ar) {
    ar.print(os);
    return os;
}
...

But beware: your class contains a raw pointer to allocated memory which is probably deleted in the class destructor. Per the rule of five, you must explicitely declare (or delete) the copy/move constructors and assignment operators.

CodePudding user response:

In your darray<T>::print function, if you change

os << m_data[i] << ", ";

to

os << m_data[i];
os << ", ";

then the compiler doesn't complain and it works fine. I don't know why.

  • Related