Why is my specialized template function called only in debug builds?

I have template functions in my C ++ 11 Xcode project, and some of them have specializations. However, I found that specializations are only called in debug builds; if I create in release they are ignored.

I have successfully created a very simple example:

special.h

#include <cstdio> struct special { template<typename T> void call(const T&) { puts("not so special"); } }; 

special.cpp

 #include "special.h" #include <string> template<> void special::call(const std::string&) { puts("very special"); } 

main.cpp

 #include "special.h" #include <string> int main() { std::string str = "hello world"; special s; s.call(123); s.call(str); } 

You can download the project (until it is in the summer of 2013) to reproduce the problem if you do not want to create it yourself. First run the project with the debug configuration, and then run it again in the release. The result that I expect is as follows:

not very special very special

And this is really what I get with the Debug build configuration. However, with the release, I get the following:

not really special not really special

This means that the specialized implementation of special::call in special.cpp has been ignored.

Why is the result incompatible? What to do to ensure that a specialized function is called in assembly versions?

+7
source share
2 answers

Your program has UB. Explicit specialization, or at least its declaration, should be visible before use. [Temp.expl.spec] Β§ 6:

If a template, a member template or a member of a class template is explicitly specialized, then a specialization must be declared before the first use of this specialization, which is an implicit instantiation, in each unit of translation into which such use occurs; no diagnostics required.

Add this ad to special.h :

 template<> void special::call(const std::string&); 

Alternatively, you can put the specialization itself in the header. However, since specialization is no longer a template, it follows the normal rules of functions and must be marked inline if it is placed in the header.

Also, be careful that specializing function templates has rather specific behavior, and it is usually better to use overloads than specializations. See the Herb Sutter article for more details.

+11
source

You have violated one definition rule (ODR). So what exactly is happening? There is no specialization in main.cpp for special::call<string> . Therefore, the compiler generates an instance of the template in this translation unit (TU), which outputs "not so special". In special.cpp there is a complete specialization, declared and defined, so the compiler puts this definition in another translation unit. Thus, you have two different definitions of the same function in two different translation units, which is a violation of ODR, which means that this behavior is undefined.

In theory, the result can be anything. Compiler error, crash, quiet online pizza order, whatever. Even other behavior in debugging and release compiles.

In practice, I assume the following: when linking a debug assembly, Linker sees the same symbol defined twice in two TUs, which is allowed only for templates and built-in functions. Due to ODR, he can assume that both definitions are equivalent and choose one from special.cpp , so you get the behavior you expect by coincidence.
During release build, the compiler builds a special::call<string> during compilation of main.cpp , so you get the only behavior observed in this TU: "not so important."

So how can you fix this?
To have only one definition for this specialization, you must define it in one TU, just as you do, but you must declare that there is a full specialization in any other TU, which means that the specialization exists in the special.h header:

 // in special.h template<> void special::call(const std::string&); 

Or, as seen more often, define it in the header, so it appears in every TU. Since fully specialized function templates are normal functions, you need to define it in a line:

 // in special.h template<> inline void special::call(const std::string&) { puts("very special"); } 
+8
source

All Articles