简体   繁体   中英

Friend function not allowed to access private member

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;

}

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.

  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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM