2

I am under the impression that you are allowed to define member functions of a class in one file and then use those functions in another file, as long as both files are compiled and sent to the linker. However, doing this gives me an undefined reference error if I use g++ (4.6.4). Interestingly, using the intel compiler (icpc 11.0) does not give an error and everything works. Is there some flag I can set in g++ to make this work, or is the intel compiler letting me get away with something I shouldn't be doing? Here is some code that reproduces my problem:

class.h:

#ifndef _H
#define _H

typedef class
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
} Test;

#endif

class.cpp:

#include "class.h"

void Test::set(int x, int y)
{
    a = x;
    b = y;
}

int Test::add(void)
{
    return a+b;
}

main.cpp:

#include <cstdio>
#include "class.h"

int main(void)
{
    Test n;
    n.set(3, 4);
    printf("%d\n", n.add());

    return 0;
}

To compile, I do:

$ g++ class.cpp main.cpp -o test 
/tmp/ccRxOI40.o: In function `main':
main.cpp:(.text+0x1a): undefined reference to `Test::set(int, int)'
main.cpp:(.text+0x26): undefined reference to `Test::add()'
collect2: ld returned 1 exit status
4
  • 5
    No need for the typedef, just do class Test {};. Commented Aug 8, 2014 at 21:53
  • 2
    _H is reserved for the implementation, chose a legal identifier. Commented Aug 8, 2014 at 21:53
  • @dyp The extra flags for g++ didn't make a difference (but I had to change -std=c++11 to -std=c++0x). I think that what I did with g++ class.cpp main.cpp was fine, but just in case I tried g++ -c class.cpp -o class.o and g++ -c main.cpp -o main.o and finally g++ class.o main.o -o test and got the same error Commented Aug 8, 2014 at 22:08
  • Oops, I misread. I'm sorry. The extra flags -Wall -Wextra -pedantic tell the compiler to produce more warnings, which can reveal some bugs. The -std=... is to be more portable, the default is gnu++98 IIRC, some extension to C++98. Commented Aug 8, 2014 at 22:24

1 Answer 1

5

Okay, this is strange, but what happened is that this construct:

typedef class
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
} Test;

while legal is not being treated semantically the same by the compiler as:

class Test
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
};

The typedef version makes your methods static to the file, as indicated in the nm output:

$ nm class.o
0000000000000024 t _ZN4Test3addEv
0000000000000000 t _ZN4Test3setEii
                 U __gxx_personality_v0

While the class Test version makes them proper methods:

$ nm class2.o
0000000000000024 T _ZN4Test3addEv
0000000000000000 T _ZN4Test3setEii
                 U __gxx_personality_v0

This is why the linker failed to find the symbols.

Edit: As to why this is happening, it seems to be due to an issue with interpreting how the Standard specifies the treatment of the typedef name as a class-name. Newer compilers do not seem to exhibit the same issue. The problem reported in this question was reproduced with g++ 4.4.7.

If you move the code in your class.cpp file into main.cpp and only compile main.cpp, things will work. Alternatively, you can inline the method definitions into class.h.

If you want to leave them as separate translation units, you need to change the class.h file so that your class is defined using the class Test way instead of using the typedef on the anonymous class.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, that worked, do you know why this happened? I thought the two versions would be equivalent.
Because you declared an anonymous class. Since the class doesn't really have a name, the C++ compiler figures it would be a violation to try to resolve a global symbol related to a method for that class. So, it made your methods file local. Here's a related question..
The name in introduced by typedef is a class-name*, while the Standard requires each member functions defined outside the class to use "its class-name" (singular). Not "one of its class-names" (plural), which would include ones created by typedef. Seems like the Standard could use some clarification on that.
@BenVoigt: Thanks, so I should change the "not kosher" wording to something indicating the proper way to deal with it is ambiguous?
But what about 7.1.3/9? "If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5)." And 3.5 specifically says that such classes and their members have external linkage.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.