Why do compiler options impact selection of template implementation? - c++

Depending on whether I compile with -O3 or without optimization the compiler does not select the same function-template-instanciation. Using gcc (Debian 8.3.0-6) 8.3.0.
Due to an oversight I have a default implementation in a function template declaration:
#pragma once
#include <iostream>
template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here
And their specialisations:
#include "func.h"
template <>
void func<1>()
{
std::cerr << "special 1\n";
}
template <>
void func<2>()
{
std::cerr << "special 2\n";
}
And the main-function.
#include "func.h"
int main(void)
{
func<1>();
func<2>();
return 0;
}
Compiling and running g++ -Wall func.cpp main.cpp -o main && ./main gives:
special 1
special 2
Using optimizations g++ -O3 -Wall func.cpp main.cpp -o main && ./main gives:
default impl
default impl
Is this expected? Is the code triggering an unexpected behavior I'm not aware of?
Thanks to #NathanOliver from the comments who made a Wandbox. Compiling with or without optimizations shows the different output.

Your code is ill-formed, no diagnostic required. So different behaviors at different optimization levels are possible.
[temp.expl.spec]
6 If a template, a member template or a member of a class
template is explicitly specialized then that specialization shall be
declared before the first use of that specialization that would cause
an implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and
either the specialization is used in a way that would cause an
implicit instantiation to take place or the member is a virtual member
function, the program is ill-formed, no diagnostic required. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.
The function template is specialized in one TU, but the other doesn't have a specialization declaration available. It's quite likely an aggressive optimizer chooses the implicit instantiation (that is available inline) instead of finding the one you created elsewhere. The solution is to declare that your specialization exists in the header.

You have undefined behaviour because of ODR issues.
ODR says that there should be only one definition for each symbol. Inline and template functions can multiple definitions but must have the same implementation, token by token. No diagnostic required if this rule is broken.
When compiling your example, the compiler will instantiate your function. Look at this:
template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here
int main(void)
{
func<1>();
func<2>();
return 0;
}
This is what the compiler sees. It cannot see other cpp files. The compiler will instantiate the templates and create additional definition for your functions.
Then your other cpp file will provide another definition that is different.
The solution to this is to forward declare the specializations in your header:
template<> void func<1>();
template<> void func<2>();
This will tell the compiler that the specializations are declared elsewhere, and not to instantiate the default one.

Related

Why do I not get a link error if I use extern template class with just one translation unit?

Consider the following code:
#include <iostream>
template<typename T>
class X
{
public:
T t;
void func1() { std::cout << "Test"; }
void func2(T x) { }
};
extern template class X<int>;
int main()
{
X<int> x;
x.func1();
}
This code compiles and links correctly (live).
However, I cannot understand why I don't get a link error due to the extern template class declaration. According to cppreference.com (emphasis mine):
An explicit instantiation declaration (an extern template) skips
implicit instantiation step: the code that would otherwise cause an
implicit instantiation instead uses the explicit instantiation
definition provided elsewhere (resulting in link errors if no such
instantiation exists). This can be used to reduce compilation times by
explicitly declaring a template instantiation in all but one of the
source files using it, and explicitly defining it in the remaining
file.
As far as I understand, the extern template class declaration should prevent the compiler from implicitly instantiating X with T = int in main. Because no instantiation actually exists, this should result in link-time errors.
Why does this code link?
As noted in the comments, the linker error is present with -O0 but goes away with -O2. When you have an explicit instantiation declaration, the compiler gets to assume the best of both worlds: the definition is available, so if the compiler feels like inlining it, it can. But if the compiler doesn't feel like inlining it, it can assume the actual function will be generated in another translation unit. So you only notice the promise to explicitly instantiate it is broken if the compiler decides not to inline the function.
This error is a One Definition Rule violation, in the form of there being zero definitions when one is required.
The reason the code runs is that the compiler optimized X<int> x; x.func1(); to std::cout << "Test";, which is allowed because the observable behaviour is the same.
This sort of error is undefined behaviour with no diagnostic required. The reason that the standard does not require a diagnostic is that it would complicate the compilation model. To have the linker produce a diagnostic for this code, the object file would have to contain extra symbols to link-check even though those symbols were not actually used in the object file.
Here is a simpler program with the same behaviour for the same reason:
void f(); int main() { int x = 0; if (x) f(); }

My template specialization differs debug version from release version, is this gcc bug?

First of all, I've got a header file for a class, an specialization declaration without definition(code samples from internet)
$ cat foo.h
template<typename T>
class foo{
public:
static void init(){
return;
}
};
template<> void foo<int>::init();
Then there're 2 implementation files for template specialization
$ cat foo_int.cpp
#include "foo.h"
#include<stdio.h>
template<>
void foo<int>::init(){
printf("init int foo\n");
}
$ cat foo_float.cpp
#include "foo.h"
#include<stdio.h>
template<>
void foo<float>::init(){
printf("init float foo\n");
}
Finally I got a main file
$ cat main.cpp
#include "foo.h"
int main(){
foo<int>::init();
foo<float>::init();
}
If I compile it without optimization and run it, it gives:
g++ foo_int.cpp foo_float.cpp main.cpp && a.out
init int foo
init float foo
If I add optimization, then the result is different:
$ g++ foo_int.cpp foo_float.cpp main.cpp -O2 && a.out
init int foo
The result is different. Some explanation from internet said this is due to some internal mechanism of "weak symbol" in gcc implementation, but my question:
Is "weak symbol"/"strong symbol" a concept of gcc/g++, or it's part of the c/c++ language specification.
If debug and release results are different, should I say this is a bug/issue of gcc/g++, in regard with "weak symbol" mechanism? As a developer, I wouldn't expect my debug version to behave differently from release version.
I tried clang, unfortunately same error. Is this an "acceptable" case for C/C++ that debug/release "should" behave so differently?
The language definition requires that you declare an explicit specialization before it is used:
If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared
before the first use of that specialization that would cause an
implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. [temp.expl.spec]/6.
There is no declaration of the explicit specialization of foo<float>::init() at the point where it is called from main, but there is an explicit specialization in foo_float.cpp, so the behavior of the program is undefined.
You've violated the one definition rule — your program contains two definitions of foo<float>::init.
One definition occurs in the compilation unit foo_float.cpp, and the other appears in the compilation unit main.cpp.
Violating the one definition rule means undefined behavior — in this case, what likely happens is:
With optimizations off, the program generates an actual function call, and the linker happened to put foo_float.cpp's version of the function in the executable.
With optimizations on, when compiling main.cpp the compiler inlined the function — naturally, it would inline main.cpp's version of the function.

do all compilers ignore generated template code when explicit specializations are available to the linker?

I've recently run into a situation while specializing templates that has made me uneasy:
foo.h:
template <class T>
void foo() {
std::cout << "This is the generic foo" << std::endl;
}
foo.cc:
#include "foo.h"
template <>
void foo<int>() {
std::cout << "This is foo<int>" << std::endl;
}
main.cc:
#include "foo.h"
int main() {
foo<int>();
}
So. I compile as follows:
g++ -c main.cc
g++ -c foo.cc
g++ -o main main.o foo.o
The output is "This is foo<int>". I like this output. But I'm worried that what I'm observing might be unique to gcc (I don't have access to other compilers so I can't check).
Here's what I think gcc is doing: When main.cc is compiled, I would expect it to emit the generic code for the foo call because it is not aware of the specialization in foo.cc. But by linking with foo.o, it uses the specialization instead because it has the same signature.
But is this bad to count on? I'm worried that other compilers (or maybe even different versions of gcc?) might mangle their signatures when they emit template code, in a way that linking with foo.o will not replace the generic action like I want it to. Is this a valid worry? I've read a lot of things that make me feel uneasy, but nothing that makes me feel confident about what is happening in my current situation.
I'm worried that what I'm observing might be unique to gcc (I don't have access to other compilers so I can't check).
You have good reasons to be worried: Your program is ill-formed, and the compiler is not even required to tell you!
Paragraph 14.7.3/6 of the C++11 Standard specifies:
If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and either the specialization is used in a way
that would cause an implicit instantiation to take place or the member is a virtual member function, the
program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit
specialization that is declared but not defined
Your specialization must be visible from the point of instantiation in order for the program to have consistent behavior. In your case, it isn't: you are relegating it in a file which is not included by other translation units.
Paragraph 14.7.3/7 Standard is quite explicit about what happens when you fail to do this:
The placement of explicit specialization declarations for function templates, class templates, member functions
of class templates, [...], can affect whether a program is
well-formed according to the relative positioning of the explicit specialization declarations and their points of
instantiation in the translation unit as specified above and below. When writing a specialization, be careful
about its location; or to make it compile will be such a trial as to kindle its self-immolation.
I guess the last sentence makes it clear.
Here, what you should do is to declare your intention to introduce an explicit specialization of your function template before any implicit instantiation of the primary template would occur. To do so, do the following:
foo.h
template <class T>
void foo() {
std::cout << "This is the generic foo" << std::endl;
}
template <> void foo<int>(); // Introduce a declaration of your
// explicit specialization right
// after you defined the primary
// template!
By introducing a declaration right after the definition of the primary template, you make sure that wherever the primary template is visible, it will be known that a full specialization for it exists, saving you from self-immolation.

Template class member specialization without declaration in header

I have a template class that I declare in a header with one method and no definition of that method in the header. In a .cc file, I define specializations of that method without ever declaring them in the header. In a different .cc file, I call the method for different template parameters for which specializations exist. It looks like this:
foo.h:
template<typename T>
class Foo {
public:
static int bar();
};
foo.cc:
#include "foo.h"
template<>
int Foo<int>::bar() {
return 1;
}
template<>
int Foo<double>::bar() {
return 2;
}
main.cc:
#include <iostream>
#include "foo.h"
int main(int argc, char **argv) {
std::cout << Foo<int>::bar() << std::endl;
std::cout << Foo<double>::bar() << std::endl;
return 0;
}
This program compiles and links successfully with gcc 4.7.2 for all C++ standards (c++98, gnu++98, c++11, and gnu++11). The output is:
1
2
This makes sense to me. Because the main.cc translation unit does not see a definition of bar() or any specializations of it, it expects the calls to bar() to use explicit instantiations of an unspecialized definition of bar() in some other translation unit. But since name mangling is predictable, the specializations in foo.cc have the same symbol names as explicit instantiations of an unspecialized definition would, so main.cc is able to use those specializations without them ever being declared in that translation unit.
My question is this: is this an accident, or is this behaviour mandated by the C++ standard? In other words, is this code portable?
The most relevant prior question that I could find is Declaration of template class member specialization, but it doesn't cover this particular case.
(In case you're wondering why this matters to me, it's because I'm using code like this as a sort of compile-time look-up table and it's a lot shorter if I don't declare the specializations.)
The Standard (C++11) requires that explicit specializations be declared (but not necessarily defined) before they are first used:
(14.7.3/6) If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the
program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. [...]
I believe that this will in practice only have an effect when your primary template definition includes the definition of the non-specialized version of one of the member functions. Because in that case, when the explicit specialization isn't declared, the existing primary definition may be used to compile the function inline into the code, and the specialization would end up not being used at link-time.
In other words, if there is no definition of the member function included in the primary template definition, your linker trick can probably be expected to work in practice, but it wouldn't conform with what the Standard says, and it can get you into real trouble as soon as you add an inline function definition to the primary template.

Template specialization for static function when compiling with g++

Why does the following code not work when compiling with:
$ g++ temp_main.cpp temp_spec.cpp
/tmp/ccirjc3Y.o:temp_spec.cpp:(.text+0x100): multiple definition of `my::say()'
/tmp/ccSo7IVO.o:temp_main.cpp:(.text$_ZN2myILi0EE3sayEv[my::say()]+0x0):
first defined here collect2: ld returned 1 exit status
I'm trying to have specialization of static function only when using
paramteter 0.
temp_gen.h:
#ifndef temp_gen_h
#define temp_gen_h
#include <iostream>
template<int N>
struct my
{
static void say();
};
template<int N>
void my<N>::say()
{
std::cout << "generic " << N << std::endl;
}
#endif
temp_spec.h:
#ifndef temp_spec_h
#define temp_spec_h
#include <iostream>
#include "temp_gen.h"
template<>
void my<0>::say()
{
std::cout << "specialized " << 0 << std::endl;
}
#endif
temp_spec.cpp:
#include "temp_spec.h"
temp_main.cpp:
#include "temp_gen.h"
int main(int argc, char* argv[])
{
my<0>::say(); //should say "specialized 0"
my<1>::say(); //should say "generic 0"
}
From within your main.cpp, you did not specialize the template class, this is because you don't include the temp_spec.h.
As Vaughn Cato pointed out (see comments), you should move the definition of the specialized method (which is no template method anymore), to temp_spec.cpp.
I think (but I'm not an expert at this) you should always put specialization directly below the generic template (in the same header file), because whenever you include this, you also want the specialized ones to be defined, otherwise you will be confused when such errors happen. You can, however, just include the temp_spec.h at the bottom of temp_gen.h.
Also, you don't need the .cpp files for the template headers, as there will never be any code to be compiled separately (template classes are always compiled at the other .cpp file which uses it, and I think duplicates will be removed when linking, causing trouble at link time again when in one case you did not specialize it, one time you did) [This paragraph only applies to the generic template class, not to (fully) specialized classes, as they require some code in their own compilation unit, see comments and the edit above.]
I believe that since your template specialization's implementation is in the header you'll need to mark it inline so the compiler/linker know you're allowed to violate the one definition rule.
Using the n3337 version of the Standard (emphasis mine):
14.7.3 Explicit specialization [temp.expl.spec]
6/ If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
Because in main the specialization of my<0>::say is not visible, implicit instantiation occurs and you end up in the case above: no diagnostic required (from the compiler).
Note that a specialization may only be declared after its generic counterpart has been declared.