1

C Example

bb.c:

#include "bb.h"
#include <stdio.h>

void bb() {
    printf("aa()...\n");
    aa();
}

main.c:

#include "aa.h"
#include "bb.h"

int main(int argc, const char** argv) {

    aa();
    bb();

    return 0;
}

aa.h:

#ifndef aa_h
#define aa_h

#include <stdio.h>

void aa() {
    printf("aa()...\n");
}

#endif // aa_h

bb.h:

#ifndef bb_h
#define bb_h

#include "aa.h"

void bb();

#endif // bb_h

C Result

Compiled with clang main.c bb.c:

duplicate symbol _aa in:
    /var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/main-OsFJVB.o
    /var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/bb-OkcMzn.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

C++ Example

b.cpp:

#include "b.hpp"

void b::do_something_else() {
    std::cout << "b::do_something_else() being called..." << std::endl;
    a a;
    a.doit();
}

main.cpp:

#include "a.hpp"
#include "b.hpp"

int main() {

    a a;
    b b;

    a.doit();
    b.do_something_else();

    return 0;
}

a.hpp:

#ifndef a_hpp
#define a_hpp

#include <iostream>

class a{
public:

    void doit() {
        std::cout << "a::doit() being called..." << std::endl;
    }

};

#endif // a_hpp

b.hpp:

#ifndef b_hpp
#define b_hpp

#include "a.hpp"

#include <iostream>

class b{
public:

    void do_something_else();

};

#endif // b_hpp

C++ Result

The above compiles fine with clang++ main.cpp b.cpp and the output to the program is:

a::doit() being called...
b::do_something_else() being called...
a::doit() being called...

Questions

  1. Why does the duplicate error not occur with the C++ version?

  2. Does the fact that the function void a::doit() is defined in the header file rather than a source file mean that the compiler will automatically inline the function?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
magnus
  • 4,031
  • 7
  • 26
  • 48
  • 1
    Your C example isn't C. In C++, `a::doit` is implicitly `inline`. – chris Jul 09 '14 at 03:37
  • Can you explain why the C example isn't C? – magnus Jul 09 '14 at 03:38
  • 2
    For starters, C doesn't have a `class` keyword. – chris Jul 09 '14 at 03:39
  • You're right, sorry I included the wrong files in the output. Fixed. – magnus Jul 09 '14 at 03:40
  • I know C and C++ are cousins, but this is really apples and oranges. C and C++ have a fair number of differences, one of them being how class methods differ from functions. As Edwin correctly pointed out, class methods are not top-level symbols (because they are not global). Scope matters in any language, but it becomes even more important in OOP languages because of the introduction of classes to the equation. It's perfectly alright to define methods of different classes with the same name. – James H Jul 09 '14 at 04:07
  • 1
    You're not understanding the question James. I'm not concerned with duplicates between `void a::doit()` and `void b::doit()`, but with the fact that `void a::doit()` is **defined** in `a.hpp` and **included** from both `main.hpp` and `b.hpp`. I think @chris has given the (somewhat brief but correct) answer in that `a::doit` is implicitly `inline`, as per question 2. – magnus Jul 09 '14 at 04:17
  • Yeah that's what I get for trying to answer a question late and when I've been running through Cal2 problems for a few hours. I did misread it. deleting answer. – James H Jul 09 '14 at 04:45

3 Answers3

2

In C++ class methods are not top-level symbols, but are effectively scoped names within their class hierarchy.

This means that you have defined in C++ two doit() methods, a::doit() and b::doit()

In C, you have attempted to define one aa() function twice.

Note that C++ will give an error too if you define the doit() method twice, within the scope of the same class.

#include <iostream>

class a {

  public:

  void doit() {
    std::cout << "hello" << std::endl;
  }

  void doit() {
    std::cout << "goodbye" << std::endl;
  }
};

leads to

ed.cpp:11:8: error: ‘void a::doit()’ cannot be overloaded
   void doit() {
        ^
ed.cpp:7:8: error: with ‘void a::doit()’
   void doit() {
        ^
Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • c++ inlines are basically optimization hints. They are best to be considered "occasionally honored". – Edwin Buck Jul 09 '14 at 03:53
  • 2
    there's a more important feature of `inline` w.r.t the one-definition rule; whether or not they are actually inlined, if the function is declared `inline` then it is allowed to have multiple definitions so long as all definitions are identical. – M.M Jul 09 '14 at 04:04
  • 1
    E.g. header file contains `void func() { }` - including the header twice from two different units causes UB, but it does not if it were `inline void func() { }`. – M.M Jul 09 '14 at 04:05
  • @MattMcNabb I agree, and thank you for the explanation; however, if you wanted to simplify such an explanation, it would basically be "don't count on an inline actually inlining without inspecting that it did" which (in my chosen parlance) can be phrased "occasionally honored" – Edwin Buck Jul 09 '14 at 04:07
  • There are two different effects of `inline`: (a) optimization hint, (b) prevent ODR-violation. Your simplified explanation doesn't address (b) at all – M.M Jul 09 '14 at 04:09
  • @MattMcNabb Again you are right, but I challenge you to find where I enumerated the effects of inline. I just said that the optimization was only a __hint__. If you took my comment to be the complete definition of the effects of an inline, you read a lot more into it than I wrote. – Edwin Buck Jul 09 '14 at 04:14
  • I would comment that you could have two doit()'s in there if it's an overloaded function. If the signature is different (return type the same but different parameters) then it would be legal. As you have it written, it is not of course however. – James H Jul 09 '14 at 04:16
  • 1
    @JamesH Right, because as I wrote it, it wasn't one name within a class hierarchy scope, but an attempt to put two identical names into that class hierarchy scope. – Edwin Buck Jul 09 '14 at 04:17
  • For a very specific language in many respects, it is a shame that a defined language feature like `inline` is only a "hint" at what the compiler should do. – magnus Jul 09 '14 at 04:20
  • @EdwinBuck you said "c++ inlines are basically optimization hints", which is false. They are optimization hints, but not "basically optimization hints". Saying "basically" implies you are giving a simplified description of their purpose, ignoring some irrelevant detail. You also said they are "occasionally honored" , however feature (b) is always honored. – M.M Jul 09 '14 at 04:22
  • @user1420752 I think it is appropriate; the compiler should be free to produce the most optimal code. If `inline` must be taken literally, it can only make the program behave worse. – M.M Jul 09 '14 at 04:23
  • @MattMcNabb If I had said "C++ inlines are optimization guarantees" or "C++ inlines are not optimization hints" you'd be all over me for being grossly wrong (and deservedly so). Think about it. – Edwin Buck Jul 09 '14 at 04:23
  • @EdwinBuck false dichotomy; just because those suggestions are even worse does not mean what you actually wrote is good! Feature (b) is always honored; feature (a) is sometimes honored. – M.M Jul 09 '14 at 04:25
  • @MattMcNabb If what I wrote wasn't a good indication that inline isn't a guarantee, then I guess there are no true Scotsmen. – Edwin Buck Jul 09 '14 at 04:33
  • A guarantee for a subset of conditions when the remaining partition in the universe of situations isn't guaranteed, is overall not a guarantee for the universe of conditions. That's my point, and it's been a pretty hard sell, for something I know you would agree with. – Edwin Buck Jul 09 '14 at 04:39
1

In your C example, aa is defined twice, which violates the "one definition rule". This would be equally true if it were C++.

In your C++ example, a::doit is defined twice, but it is implicitly declared inline. Member functions defined within a class are implicitly inline per [dcl.fct.spec]/3:

A function defined within a class definition is an inline function. ...

inline functions are an exception to the one definition rule (in fact, this is the only meaning of inline required by the standard) per [basic.def.odr]/5.

There can be more than one definition of a ... inline function with external linkage (7.1.2) ... in a program, provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...

The requirements essentially boil down to a requirement that the definitions be identical in every translation unit where they appear.

Had you declared aa as inline, similar rules would have applied and your code would have compiled and worked as expected.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
0

Why does the duplicate error not occur with the C++ version?

Because there is no duplication. C++ member functions are scoped by the class they are defined in. b::doit() isn't a duplicate of a::doit().

Does the fact that the function void a::doit() is defined in the header file rather than a source file mean that the compiler will automatically inline the function?

No, but it means it is possible.

user207421
  • 305,947
  • 44
  • 307
  • 483