4
votes

In an application I'm developing I have a template function like this:

template<class T>
void CIO::writeln(T item)
{
    stringstream ss;
    ss << item << '\r' << endl;
    write(ss.str());
}

This function is called from several places, with T = const char* and T=std::string. With CodeSourcery Lite 2008.03-41 (GCC 4.3.2) this compiled and linked fine with -O3 compiler flag. However, since I changed to CodeSourcery Lite 2012.03-57 (GCC 4.6.3), compiling with -O3 is OK, but then linking fails with undefined reference to void CIO::writeln<std::string>(std::string). With -O2 or lower everything is OK, linking succeeds.

I had a deeper look into this and I discovered something strange in the assembly output: when compiling with -O2, I can find two specializations of the function: one for const char* (_ZN3CIO7writelnIPKcEEvT_) and one for std::string (_ZN3CIO7writelnISsEEvT_), but when compiling with -O3, the second specialization is missing, which explains the linking error.

Is this a compiler bug? Is this some weird optimization turned evil?

Thanks in advance!

Edit: this function is in a source file. Following Mike Seymour's comment, I moved it to the header and everything's fine now. I admit that I should've realized this earlier. Nevertheless, it still frightens me that a language rule is checked or not depending on an optimization flag.

1
Is that definition in a header, or a source file? Template definitions usually need to be in headers, so they're available wherever they're instantiated. See stackoverflow.com/questions/495021.Mike Seymour
I worked with gcc 4.6 for a while, and wouldn't be surprised if it was a compiler's bug. That branch is full of bugs (at least 4.6.1)BЈовић
@Mike: now that you mentioned it, it kinda makes sense...Istvan Knebli

1 Answers

1
votes

Unlike what the other answer says, this is probably not a compiler bug.

One of the optimisations that gets enabled by -O3 is function inlining. What I think is happening is:

Source file 1 is calling CIO::writeln without having its definition available. It is compiled to object file 1.

Source file 2 is calling CIO::writeln while having its definition available. It is compiled to object file 2.

Object file 1 will only be usable if object file 2 contains the definition of CIO::writeln. If the call in source file 2 gets inlined, object file 2 won't contain a definition for it. If the call does not get inlined, a definition will be available.

The solution given in the comments, move the definition to a header file, is correct.