puzzled about global classes

I am puzzled about the following section of B. Stroustrup (2nd ed)
"Names of global classes must be unique in a program and must refer to unique definitions. So even though the two class definitions are identical this is an error:
1
2
3
4
5
6
7
//file 1.c:
struct S {int a; char b;};
extern void f(S*);

//files 2.c
struct S {int a; char b;};
void f (S* p){ /* ... */}


So how to do this right (i.e. not in violation with the standard)? (My compiler doesnt complain about this, but this is also mentioned in the book). So how to conform with the standard if I want to access a struct/class in 2 files?

Can you post the page where you found this?

I'll try to find my copy of the book and take a look. It certainly doesn't appear in the 3 edition as presented on Safari Books, and the code looks ok to me. I'll like to take a look at the context.
It is end of page 111/ beginning of 112. Chapter 4.2
(4: "Functions and Files", 4.2: "Linkage")
Related to this might be the reference manual r.3.1, that struct S {int a; int b;}; is a definition, and only one definition for each class is allowed; but I am guessing.

Thanks for helping!
True. Otherwise it leads to many issues. One of them:

Consider two .cpp files with same class name but with different data member (just to show how it can lead to problem). Both has class S. First .cpp has a data member "a" of type array of int of 50 elements. Second .cpp has a data member "a" of type array of int of 100 elements. Assume both the .cpp file creates a global object of type "class S". This triggers the call to the default constructor. Refer to the output which shows the real problem (constructor of class S of second .cpp is never called in the first method of build/compilation, whereas the constructor of class S of first .cpp is never called in the second method of build/compilation). So here the functionality differs depending on the order of linking.

class_s_main.cpp:
1
2
3
4
5
6
7
#include <iostream>

int main()
{
  std::cout << "In file " << __FILE__ << " at line " << __LINE__ << std::endl;
  return 0;
}


class_s_f1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

class S
{
  private:
    int a[50];    
  public:
    S()
    { 
      std::cout << "In file " << __FILE__ << " at line " << __LINE__ << std::endl;
      std::cout << "Size of data member a: " << sizeof(a) << std::endl;
    }
};

S sobj1;


class_s_f2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

class S
{
  private:
    int a[100]; // Note the difference in size.  
  public:
    S()
    { 
      std::cout << "In file " << __FILE__ << " at line " << __LINE__ << std::endl;
      std::cout << "Size of data member a: " << sizeof(a) << std::endl;
    }
};

S sobj2;




$g++ class_s_f1.cpp class_s_f2.cpp class_s_main.cpp
$./a.out
In file class_s_f1.cpp at line 10
Size of data member a: 200
In file class_s_f1.cpp at line 10
Size of data member a: 200
In file class_s_main.cpp at line 5



$g++ class_s_f2.cpp class_s_f1.cpp class_s_main.cpp
$./a.out
In file class_s_f2.cpp at line 10
Size of data member a: 400
In file class_s_f2.cpp at line 10
Size of data member a: 400
In file class_s_main.cpp at line 5

The post shows both classes being identical, which is the same code that the compiler would see if the declaration was placed in a shared header file.

Obviously, if the declarations were different, you'd have problems. But that isn't the case here.
The way to avoid such issues (like the one shown above) is to keep the class definition in a header file and include it in the .cpp files.

class_s_def.h
1
2
3
4
5
6
7
8
9
10
11
class S
{
  private:
    int a[100];
  public:
    S()
    { 
      std::cout << "In file " << __FILE__ << " at line " << __LINE__ << std::endl;
      std::cout << "Size of data member a: " << sizeof(a) << std::endl;
    }
};


class_s_f1.cpp
1
2
3
4
#include <iostream>
#include "class_s_def.h"

S sobj1;


class_s_f2.cpp
1
2
3
4
#include <iostream>
#include "class_s_def.h"

S sobj2;


class_s_main.cpp
1
2
3
4
5
6
7
#include <iostream>

int main()
{
  std::cout << "In file " << __FILE__ << " at line " << __LINE__ << std::endl;
  return 0;
}


Note: The link order does not matter here because the class definition is unique across compilation units (.cpp files).


$g++ -I. class_s_f2.cpp class_s_f1.cpp class_s_main.cpp
$./a.out
In file class_s_def.h at line 9
Size of data member a: 400
In file class_s_def.h at line 9
Size of data member a: 400
In file class_s_main.cpp at line 5

$g++ -I. class_s_f1.cpp class_s_f2.cpp class_s_main.cpp
$./a.out
In file class_s_def.h at line 9
Size of data member a: 400
In file class_s_def.h at line 9
Size of data member a: 400
In file class_s_main.cpp at line 5
Thanks for the input. I understand that different global class definitions are an error, which can be avoided by placing it in a header file.

So does anybody know if there was a change in the standard to allow having it in a header file? How was this problem solved before the change? The reference manual of Stroustrup cannot have a mistake, or has it?

So does anybody know if there was a change in the standard to allow having it in a header file?

No, there was no change. Having the definition of a class in a header file and including that header file in multiple source files means that those source files are referring to the same definition of the class, thus there is no violation of the ODR.

If you wish to place a definition in a source file, put it in an anonymous namespace and not at the global scope.
Having copies of same class (supposed/thought of by user as part of program logic) in different source file will lead to problems when one of the class copy gets modified (say a data member addition/deletion etc).
Thanks. Especially to kbw for the hint that the 3rd edition doesnt say so. I just bought the 3rd edition (second hand of course) and there is exactly the same example as posted, but the text says:
The ODR says that this example is valid.

So most likely the standard has changed/evolved (by removing a silly restrictionb) when formalizing C++ for ISO/IEC 14882, but of course probably only one person would know.
So most likely the standard has changed/evolved (by removing a silly restrictionb) when formalizing C++ for ISO/IEC 14882, but of course probably only one person would know.


Why would only one person know? The example was valid in the 1998 standard.

See http://nepsweb.co.uk/langstand/isoCPP/ISO14882/basic.html

3.2.5, in particular.

[edit:] And now, in your last post, I see you're talking about the ODR pre-standard. Naming the book instead of just the author might've made that more obvious in the OP.. but, what's the point? As long as there's been a standard, the example has been legal.
Last edited on
Why would only one person know? The example was valid in the 1998 standard.

I dont object that it was valid in 1998. The 2nd edition of Stroustrups book is from 1991, afaik there was no 'official' standard, so I believe this book with the reference manual was the defacto 'standard'. (pls correct me if I am wrong on any of these points)
According to the text it was not valid in 1991. But probably only the author (=inventor of C++) would know if he would have considered it really wrong, or if it is a typo/unclear writing.

ps: There is no ODR in the 1991 reference manual. My wild guess is it was introduced for exactly this problem.
Last edited on
Yes. As noted in the edit above, I didn't realize you were referring to pre-standard C++ (and portions of the OP don't make much sense after realizing it.)

Stroustrup has written more than one book. ;)
I didn't realize you were referring to pre-standard C++

Well, neither did I; since the 2nd ed. contains a reference manual, I assumed there was some kind of standard at that time.
Sorry for the confusion.
Topic archived. No new replies allowed.