0

Suppose I have a class template A, and non-template class B inheriting from A. While A compiles OK, compiling B triggers linker errors. Specifically,

A.hpp

#ifndef A_HPP_INCLUDED
#define A_HPP_INCLUDED

template <typename T>
class A { 
public:
    A();
    A(A<T>&& );
    virtual ~A();

    /* ... */
protected:
    T x;
};
#include A.tpp
#endif

A.tpp

#include "A.hpp"

template <typename T>
A<T>::A() { ... }

template <typename T>
A<T>::A(A<T>&& other)
   : x(std::move(other.x)) { }

template <typename T>
A<T>::~A() { ... }

testA.cpp

#include "A.hpp"

int main() {

    A<std::string> a;

    /* ... */

    return 0;
}

Compiling testA.cpp as follows is successful:

$ g++ -std=c++11 testA.cpp <- OK

Next up is non-template class B inheriting from A:

B.hpp

#ifndef B_HPP_INCLUDED
#define B_HPP_INCLUDED
#include "A.hpp"
class B
    : public A<std::string> {
public:
    B();
    virtual ~B();

    static A<std::string> foo();
};
#endif

B.cpp

#include "B.hpp"

B::B()
    : A(std::move(foo())) { }

B::~B() { }

A<std::string> B::foo() { ... }

testB.cpp

#include "B.hpp"

int main() {
    B b;

    /* ... */

    return 0;
}

Compilation of testB.cpp seems to go ok, but the linker is not a happy camper:

Attempt 1

$ g++ -std=c++11 testB.cpp   
Undefined references to B(), and ~B()
collect2: error: ld returned 1 exit status

Attempt 2

$ g++ -std=c++11 testB.cpp B.cpp
Undefined reference to B.foo()
collect2: error: ld returned 1 exit status

Any help/ideas are greatly appreciated. Mr. ld kept me up most of the night, and is threatening my sanity.

Edit

Thank you Mike Seymour! This minimal example was not a true rendition of the real code, as indeed a qualifier was missing in the implementation, and was the wrench in the gear.

AlmostSurely
  • 552
  • 9
  • 22
  • 1
    besides dots (no pun intended), I see nothing wrong with your code. – BЈовић Nov 05 '13 at 13:10
  • 1
    the 2nd attempt shows `Undefined reference to B.foo()`, meaning you didn't implement the `foo` static function – BЈовић Nov 05 '13 at 13:12
  • 2
    The first attempt won't work, since it's not linking with the file that defines the member functions of `B`. The second works for me, once I fix all the compile errors in the posted code. Perhaps you could post example code that does compile, and does reproduce the error. – Mike Seymour Nov 05 '13 at 13:14
  • 1
    Also, is that the exact error message? If I remove the definition of `foo`, then my version of GCC says "undefined reference to `B::foo()'" – Mike Seymour Nov 05 '13 at 13:16
  • Thanks for the response, this is not the actual error message - I translated from the actual errors for the sake of a minimal example (after the all nighter :) ). Gonna try bobah's suggestion, before posting the actual code and risking confusing everyone as much as myself :) – AlmostSurely Nov 05 '13 at 13:19
  • 1
    @AlmostSurely: It would be nice if you published at least the line containing `foo`invocation. – Ad N Nov 05 '13 at 13:22
  • possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Mike Seymour Nov 05 '13 at 14:42
  • 1
    Presumably, the real function corresponding to `B::foo` is `ParsedData::parsedStream`, which is a template and so needs to be defined in a header. – Mike Seymour Nov 05 '13 at 14:42
  • @MikeSeymour: Either you are a magician, or you had access to an information we did not have... Anyway, that does not make a very good SO entry. – Ad N Nov 05 '13 at 18:58
  • @Ad N, Hmm, I removed the actual code, since it's part of my final semester project - not Mike's fault at all. The problem seems to have been the missing qualifier in the implementation of `B::foo()`, which was not accurately conveyed in the minimal example. i.e., in the "real" `B.cpp`, I had `foo()` in the signature, rather than `B::foo()`. I appreciate all your responses :) – AlmostSurely Nov 05 '13 at 19:59
  • I also neglected to mention in the OP, (credit severe sleep deprivation), that `B::foo()` is itself a template, and as per Mike's advice, ought to be in its own header to avoid future linker complaints. – AlmostSurely Nov 05 '13 at 20:09

3 Answers3

3

I suggest you compile and link in two distinct passes:

g++ -c -std=c++11 -o B.o B.cpp
g++ -c -std=c++11 -o TestB.o TestB.cpp
g++ -o test.exe B.o TestB.o

If what I suggest breaks it will at least be clear what is missing where and you will be able to debug individual object files with nm.

bobah
  • 18,364
  • 2
  • 37
  • 70
1
  $ g++ -std=c++11 testB.cpp   
  Undefined references to B(), and ~B()

This one is obviously wrong (I just say that because I do not see why you are making this attempt). You compile only one translation unit, that uses symbols defined in another (B.cpp) that is not compiled.

I think you should publish the actual content behind the ...in testB.cpp to help find the problem. It could be in your usage of foo()

By anticipation : since foois a static member method of your class, your invocation in the test should look like :

B::foo();
Ad N
  • 7,930
  • 6
  • 36
  • 80
1

In your real code, ParsedData::parsedStream is a template, and so should be defined in a header so that it's available in every translation unit that might need to instantiate it.

Also, you don't define it even in the .cpp file, since you left off the ParsedData:: qualifier and instead declared a different template at namespace scope.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644