friends, inheritance and forward declaration

Hello,

I am trying to code the example given in "Ruminations on C++" Koenig, Moo, §8.3 but I don't get it to run like shown in the book.

Here is my minimized example:
compile:
g++ main.cpp expr.cpp

main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "expr.hpp"

using std::cout;
using std::endl;

int main(int argc, char** argv)
{
   Expr t(5);

   cout << t << endl;

   return 0;
}


expr.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef EXPR_H
#define EXPR_H

#include <iostream>
#include "expr_node.hpp"

class Expr
{
   friend std::ostream& operator<<(std::ostream&, const Expr&);

   public:
      Expr(int);
      ~Expr();

   private:
      Expr_node* p;
};

#endif 


expr.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "expr.hpp"

#include "int_node.hpp"

Expr::Expr(int k)
{
   p = new Int_node(k);
}
//------------------------------------------------------------------------------ 
Expr::~Expr()
{
   if (--(p->use) == 0) delete p;
}
//------------------------------------------------------------------------------ 
std::ostream& operator<<(std::ostream& o, const Expr& e)
{
   e.p->print(o);
   return o;
}


expr_node.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef EXPR_NODE_H
#define EXPR_NODE_H

#include <iostream>
#include "expr.hpp" //has no effect because of GUARD

class Expr_node
{
   class Expr;
   friend std::ostream& operator<<(std::ostream&, const Expr&); //-> op<< needs access
   friend class Expr; //-> Expr class needs access to "use" and ~Expr_node() 

   protected:
      Expr_node(): use(1) { }
      virtual void print(std::ostream&) const = 0;
      virtual ~Expr_node() { }

   private:
      int use;
};

#endif 


int_node.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef INT_NODE_H
#define INT_NODE_H

#include <iostream>

class Int_node: public Expr_node
{
   friend class Expr; //-> Expr class constructs Int_nodes via Int_node(int)

   private:
      int n;
      void print(std::ostream& o) const {o << n;}
      Int_node(int k): n(k) { }
};

#endif 


The program compiles and runs if I
- remove the forward declarion
- remove the friendship to operator<<
- declare print() as public

but there is no reason why print() should be public and it is protected in the book! If I set print() as protected, I need the friendship with operator<< because this function uses print() in expr.cpp line 17:
e.p->print(o);

As the friendship declaration has the Expr type in its arguments I need to forward declare class Expr. (otherwise "Expr doesn't name a type" error).
Doing so causes the compiler error:
expr.cpp:7:22: error: ‘Int_node::Int_node(int)’ is private within this context


BUT: class Expr is friend of Int_node.
I do not understand how the forward declaration in one class (expr_node) can cause a problem in another class (expr).

Can someone see what is going wrong here?
Thanks in advance.
Last edited on
main() cannot see a a declaration of operator<<.

printf() doen't need to be public, as it's used by a friend of Expr.
I got the code you give above to compile by making the following changes:

- int_node.hpp:
  - add #include "expr_node.hpp"
- expr_node.hpp:
  - remove #include "expr.hpp"
  - add class Expr; in its place
- expr.hpp
  - remove #include "expr_node.hpp"
  - add class Expr_node; in its place

Last edited on
This version compiles and run:

int_node.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef INT_NODE_HPP
#define INT_NODE_HPP


#include "expr_node.hpp"
#include <iostream>


class Expr;


class Int_node: public Expr_node {
private:
    int n;
    Int_node(int);
    void print(std::ostream&) const override final;

//friend:
    friend class Expr;
};


Int_node::Int_node(int n_arg)
    : n { n_arg }
{
}


void Int_node::print(std::ostream& os) const
{
    os << n;
}


#endif // INT_NODE_HPP 



expr_node.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef EXPR_NODE_HPP
#define EXPR_NODE_HPP


#include <iostream>


class Expr;


class Expr_node {
protected:
    Expr_node();
    virtual ~Expr_node() = default;
    virtual void print(std::ostream&) const = 0;

private:
    int use;

//friend:
    friend class Expr;
    friend std::ostream& operator<<(std::ostream&, const Expr&);
};


Expr_node::Expr_node()
    : use { 1 }
{
}


#endif // EXPR_NODE_HPP 



expr.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef EXPR_HPP
#define EXPR_HPP


#include <iostream>


class Expr_node;


class Expr {
public:
    explicit Expr(int);
    ~Expr();

private:
    Expr_node* p;

//friend:
    friend std::ostream& operator<<(std::ostream&, const Expr&);
};


#endif // EXPR_HPP 



expr.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "expr.hpp"
#include "int_node.hpp"


Expr::Expr(int p_arg)
    : p { new Int_node(p_arg) }
{
}


Expr::~Expr()
{
   if ( --(p->use) == 0) { delete p; }
}


std::ostream& operator<< (std::ostream& o, const Expr& e)
{
   e.p->print(o);
   return o;
}



main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
#include "expr.hpp"
#include <iostream>


int main()
{
   Expr t(5);

   std::cout << t << '\n';

   return 0;
}


Compilation instruction:
 
g++ -std=c++2a -Werror -Wall -Wextra -Wpedantic -Wshadow -O2 expr.cpp main.cpp -o main.exe


Output:
5

Enoizat is a cunt. :-(
@dutch,
just to say I’m not the one who reported your post — even if I disagree with it :)
Thanks,

as far as I can see there were two problems:

1) expr.hpp does not need the definition of expr_node. A forward declaration is enough because expr_node is only used as a pointer.

2) forward declarations must be outside of the class (not sure if this is always true).

Now it runs, and I think I understood something more...
Thanks for the answers.
Topic archived. No new replies allowed.