Same class name in different C++ files
Asked Answered
E

4

27

If two C++ files have different definitions of classes with the same name, then when they are compiled and linked, something is thrown out even without a warning. For example,

// a.cc
class Student {
public:
    std::string foo() { return "A"; }
};
void foo_a()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}

When compiled and linked together using g++, both will output "A" (if a.cc precedes b.cc in the command line order).

A similar topic is here. I see namespace will solve this problem but I don't know why the linker doesn't even shoot a warning. And if one definition of the class has extra function that isn't defined in another, say if b.cc is updated as:

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
    std::string bar() { return "K"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << stu.bar() << std::endl;
}

Then stu.bar() works well. Thanks to anyone who can tell me how the compiler and linker work in such situation.

As an extra question, if classes are defined in header files, should they always be wrapped with unnamed namespace to avoid such situation? Is there any side effects?

Erasmoerasmus answered 20/5, 2012 at 8:31 Comment(1)
Could it be that when it is compiled, the compiler looks and see's that there already is a 'student' class so it ignores it?Ginter
T
25

This is a violation of the one definition rule (C++03, 3.2/5 "One definition rule"), which says (among other things):

There can be more than one definition of a class type (clause 9), ... in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens;

If you violate the one definition rule, the behavior is undefined (which means that strange things can happen).

The linker sees multiple definitions of Student::foo() - one in a's object file and one in b's. However it doesn't complain about this; it just selects one of the two (as it happens, the first one it comes across). This 'soft' handling of duplicate functions apparently happens only for inline functions. For non-inline functions, the linker will complain about multiple definitions and will refuse to produce an executable (there may be options that relax this restriction). Both GNU ld and MSVC's linker behave this way.

The behavior makes some sense; inline functions need to be available in every translation unit they're used in. And in the general case they need to have non-inline versions available (in case the call isn't inlined or if the function's address is taken). inline is really just a free pass around the one-definition rule - but for it to work, all the inline definitions need to be the same.

When I look at dumps of the object files, I don't see anything obvious that explains to me how the linker knows that one function is permitted to have multiple definitions and others aren't, but I'm sure there's some flag or record which does just that. Unfortunately, I find that the workings of the linker and object file details aren't particularly well documented, so the precise mechanism will probably remain a mystery to me.

As for your second question:

As an extra question, if classes are defined in header files, should they always be wrapped with unnamed namespace to avoid such situation? Is there any side effects?

You almost certainly don't want to do this each class would be a distinct type in each translation unit, so technically instances of the class they couldn't be passed from one translation unit to another (by pointer, reference or copying). Also, you'd end up with multiple instances of any static members. That probably wouldn't work well.

Put them in different, named namespaces.

Toehold answered 20/5, 2012 at 8:39 Comment(5)
Different unnamed namespaces would also work. Named namespaces are preferable, but at least he can eliminate UB and get things working by putting namespace { } around the offending code.Alicea
@Potatoswatter: Unnamed namespaces would work in this case because the classes are pretty simple. It might even work in most cases, but it's not something that you can do in general without regard for the design of the class.Toehold
I've updated the answer with some details about why (and when) the linker doesn't complain about multiple definitions of some functions.Toehold
Then should the author always be sure that in a single linking unit there is no duplicated mangled class functions in differenct source files? What if author 1 creates ClassFoo::bar() in source1.cpp and author 2 creates ClassFoo::bar() in source2.cpp? The linked unit is just corrupted? Don't understand why doesn't the build system just warn about duplicated class methods--some type of global checking.Torrez
When I look at dumps of the object files, I don't see anything obvious that explains to me how the linker knows that one function is permitted to have multiple definitions and others aren't, but I'm sure there's some flag or record which does just that.. when you use readelf -Ws on a.o and b.o,you will be noticed that the bind type of these symbols are WEAK. the linker will report error while link objects which has duplicated GLOBAL and not UND symbol. @Michael BurrArak
O
3

You violated the one definition rule for class definitions and the language specifically forbids doing this. It's not required for the compiler/linker to warn or diagnose, and such a scenario is certainly not guaranteed to work as expected in this case.

Out answered 20/5, 2012 at 8:38 Comment(2)
This doesn’t really answer the question: why doesn’t the linker warn?Piccadilly
The linker knows nothing about C++, so will link the first thing fitting. It will only complain if it cannot find a symbol or if encounters contrasting definition.Sev
U
0

I think your "extra" question is a clue to the main question.

If I understand your extra question, then I think you do not want to wrap them in a namespace, since if you #include the same class into multiple .cc files, then you probably want to use just one copy of each method, even if they are defined inside the class.

This (sort of) explains why you only get one version of each function in your main example. I expect the linker is just assuming that the two functions are identical -- generated from the same #included source. It would be nice if the linker would detect when they were different and issue a warning, but I guess that is hard.

As Mark B indicates, the practical answer is "Just don't go there".

Uranalysis answered 20/5, 2012 at 8:40 Comment(0)
B
0

Apart from violation of one definition rule , you don't see compiler complaining because of Name mangling in C++

Edit : As pointed out by Konrad Rudolph : Mangled names in this case would be same.

Bulimia answered 20/5, 2012 at 8:48 Comment(4)
I don’t see what name mangling has got to do with this. Can you explain?Piccadilly
IMHO , as the class definition is in different files , inspite being with the same name the mangled names would be different , thus uniqueness is maintained. Correct me if i am wrong !Bulimia
You are wrong. The mangled names are the same. In fact, if they weren’t, OP wouldn’t have the problem that he has.Piccadilly
This is interesting - when two object files have the same (mangled) name for a C++ member function, the linker doesn't complain and just picks one of the two to link into the executable. However, if the name is for a C function, the linker complains about multiple definitions. Both gnu ld and MSVC's linker behave this way. I can't see an obvious difference in the records for these situations (other than the ugly name mangling). I wonder what the linker sees that makes it more 'forgiving' of the C++ duplicates. I don't see the mechanism used.Toehold

© 2022 - 2024 — McMap. All rights reserved.