-2

I thought friend functions had access to all members. Even in this question it worked:
C++ friend function can't access private members

The answer given in that question seems identical to my code, and his compiled fine while mine just says array_ is pivate. Anyone know why?

.h:

#ifndef matrix_h
#define matrix_h

#include <iostream>
using namespace std;

template <typename Comparable>
class matrix
{
    private:
        size_t num_cols_;
        size_t num_rows_;
        Comparable **array_;

    public:
        friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
        size_t NumRows();
        size_t NumCols();
};
#endif

.cpp:

#include <iostream>
#include "matrix.h"

using namespace std;

template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs){
    size_t c = rhs.NumRows();
    size_t d = rhs.NumCols();
    for (int i = 0; i < c; i++){
        for (int j = 0; j < d; j++){
            o << rhs.array_[i][j];         //not allowed
        }
        o << endl;
    }
    return o;
}

template <typename Comparable>
size_t matrix<Comparable>::NumRows(){
    return num_rows_;
}

template <typename Comparable>
size_t matrix<Comparable>::NumCols(){
    return num_cols_;
}


int main(){
    matrix<int> a;
    cout << a << endl;

}
Community
  • 1
  • 1
user3444650
  • 117
  • 9
  • You've declared the friend function to take a `const matrix &`, but its definition takes a `matrix &`. They aren't the same function. – user657267 Sep 10 '15 at 01:17
  • 1
    Also: [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – NathanOliver Sep 10 '15 at 01:18
  • If I make them both const, or remove const than I get an undefined reference error. Undefined reference to operator< – user3444650 Sep 10 '15 at 01:22
  • Danger: [Rule of Three](http://en.cppreference.com/w/cpp/language/rule_of_three) violation. Your matrix will have unfortunate and probably fatal behaviour if copied or moved. – user4581301 Sep 10 '15 at 01:29
  • I have a constructor/destructor/copy overload. I just didn't include it since it's irrelevant. Also am I not supposed to use a .cpp? – user3444650 Sep 10 '15 at 01:31
  • In order for a template to work, everyone using the template needs to be able to see the functions they need to specialize. If they in the header, that's no problem. If they are in a CPP file... Including the CPP file is a bad idea. – user4581301 Sep 10 '15 at 01:34

2 Answers2

0

Say you use const in both places and add const to the declarations of numRows and numCols too. Then what's the problem? Well...

You think it's identical, but your code has a template. And the friend declaration

friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);

is not a template, so it doesn't match the definition

template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs){ // ...

which is a template. In fact gcc will give a warning:

matrix.h:16:79: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const matrix<Comparable>&)’ declares a non-template function [-Wnon-template-friend]
         friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
                                                                               ^
matrix.h:16:79: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 

It's tempting to just friend all specializations, like this:

template <typename T>
friend ostream& operator<< (ostream& o, const matrix<T> & rhs);

Unfortunately, this won't work for the reasons explained here: Why can templates only be implemented in the header file? You'll be able to compile matrix.cpp, but not a separate driver program, like this:

#include <iostream>
#include "matrix.h"
using namespace std;
int main() {
    matrix<int> m;
    cout << m << endl;
}

You get an undefined reference error. Instead, you really should just define your entire matrix class in the header and ditch the .cpp file.

It should be pointed out that this still has a problem: you can call this operator<< just fine, but you can't, say, take its address, because it can only be found by argument-dependent lookup.

auto ptr = static_cast<ostream&(*)(ostream&, const matrix<int>&)>(operator<<); // error

For it to be found by unqualified lookup, it must have a matching declaration at namespace scope. And it is actually impossible to write such a declaration (the syntax of C++ doesn't have any way to do it)! To fix this, we need to turn operator<< into a function template, defined inline:

template <typename Comparable>
class matrix {
    // ...
    template <typename T>
    friend ostream& operator<<(ostream& o, const matrix<T>& rhs) {
        // ...
    }
    // ...
};
// namespace-scope declaration
template <typename T>
ostream& operator<<(ostream& o, const matrix<T>& rhs);

Now the above code taking the address of the operator will work.

Community
  • 1
  • 1
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
0
  1. The compiler complains because the function you implement is different with the one you declare(in declaration rhs is decorated by const, in implementation it isn't).
  2. After you add const in implementation, the compiler complains "Undefined reference" because the declaration and the implementation is still not the same. The implementation is a template function, the decalartion is not.

    #ifndef matrix_h
    #define matrix_h
    
    #include <iostream>
    using namespace std;
    
    template <typename Comparable>
    class matrix
    {
        private:
            size_t num_cols_;
            size_t num_rows_;
            Comparable **array_;
    
        public:
            template<typename T>
            friend ostream& operator<< (ostream& o, const matrix<T> & rhs);
            size_t NumRows() const;
            size_t NumCols() const;
    };
    
    template <typename Comparable>
    ostream& operator<< (ostream& o, const matrix<Comparable> & rhs){
        size_t c = rhs.NumRows();
        size_t d = rhs.NumCols();
        for (int i = 0; i < c; i++){
            for (int j = 0; j < d; j++){
                o << rhs.array_[i][j];         //not allowed
            }
            o << endl;
        }
        return o;
    }
    
    template <typename Comparable>
    size_t matrix<Comparable>::NumRows() const{
        return num_rows_;
    }
    
    template <typename Comparable>
    size_t matrix<Comparable>::NumCols() const{
        return num_cols_;
    }
    
    #endif
    
Zhenghua
  • 1
  • 2