0

I have a class template Foo<T>, deriving from FooBase.

Foo.h:

class FooBase
{
public:
    virtual ~FooBase() {}
};

template <class T>
class Foo : public FooBase
{
public:
    T t;
};

Then, I have two translation units instantiating Foo with different T's. The class used as template parameter is called T in both translation units, but protected by an unnamed namespace. Each translation unit defines a free function.

Test1.h:

void test1();

Test1.cpp:

#include "Test1.h"
#include "Foo.h"
#include <cassert>

namespace { class T {}; }

void test1()
{
    Foo<T> foo;
    FooBase & base = foo;
    assert(&base == &foo); // To be able to breakpoint here
}

Test2.h:

void test2();

Test2.cpp:

#include "Test2.h"
#include "Foo.h"
#include <cassert>

namespace { class T { int x; }; }

void test2()
{
    Foo<T> foo;
    FooBase & base = foo;
    assert(&base == &foo); // To be able to breakpoint here
}

Finally, I have a main that calls both free function, and I link all three translation units.

main.cpp:

#include "Test1.h"
#include "Test2.h"

int main()
{
    test1();
    test2();
}

Question: Is this legal C++11 ?

I thought it should be since the name conflict is resolved by the unnamed namespace. However, I am now in doubt because:

  1. GDB ( v7.7.1, on Kubuntu 14.04 64bits) is really confused about this
  2. I have weird bugs in my real-life case which I can't track down

GDB

GDB issues those warnings on test2():

can't find linker symbol for virtual table for `FooBase' value
can't find linker symbol for virtual table for `Foo<(anonymous namespace)::T>' value

And is not able to determine that base is actually of dynamic type Foo and therefore inspect its member t. Also, instead of the nice

<vtable for Foo<(anonymous namespace)::T>+16>

which I get on test1, I get the following on test2:

<_ZTV3FooIN12_GLOBAL__N_11TEE+16>

AND even much much worse, when inspecting foo.t on test1, it finds the member foo.t.x which should only exist in test2!

See below for screenshots within QtCreator:

enter image description here

enter image description here

All above issues are resolved if I name the template parameter T1 in Test1.cpp and T2 in Test2.cpp.

Despite GDB confusion, on all variants I tried around this minimal example, the program always seems to behave correctly (GCC 4.8.2). For instance printing sizeof(T) via a virtual method called from base correctly returns 1 and 4 for test1 and test2 respectively (while it prints 1 and 1 if I remove the unnamed namespace, due to the actual name conflict, which I know makes the code not legal C++).

Real-life case

On my real-life case, I have a segfault that:

  1. always occurs with "same name in global scope" (obviously)
  2. occurs unpredictably with "same name under unnamed namespace"
  3. never occurred with manually assigned unique names

I don't know if it's because assigning unique names manually fix the issue (could it, since they were already in unnamed namespace??), or if my code is still broken somewhere else and I'm just "lucky" (i.e., a hidden undefined behavior, which is very scary). I've spent two days trying to reduce to a minimal example that still crashes this way, but failed. I only managed to get minimal examples that work, or my actual code that crashes sometimes.

Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59
  • GDB is known to not be perfect, so GDB confusion over anything means very little. – n. m. could be an AI Mar 06 '16 at 12:39
  • Does your program by any chance use `typeid(Foo).name()`? – n. m. could be an AI Mar 06 '16 at 12:55
  • @n.m. Thc for the answer. No, I'm not using typeid() :-/ – Boris Dalstein Mar 06 '16 at 13:23
  • I could not understand how you can check if the pointers of the base class and the derived class are same or not. – mustafagonul Mar 06 '16 at 13:27
  • 1
    @mustafagonul This is irrelevant to the question, but here is the explanation: here, `base` is a reference to `foo`, which means that they refer to the same object in memory. `&base` is of type `FooBase*`, and `&foo` is of type `Foo*`. When I ask for comparison, then the `Foo*` is implicitely upcasted to `FooBase*`. So you get two `FooBase*` to compare, which point to the same object in memory, so the assert successfully passes. – Boris Dalstein Mar 06 '16 at 13:43

0 Answers0