Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

xcode

I have templated functions in my C++11 Xcode project and some of them have specializations. However, I have discovered that the specializations only get called in debug builds; if I build 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 somewhere this summer of 2013 at least) to reproduce the issue if you don't want to create it yourself. First run the project with the debug configuration, then run it again in release. The output that I expect is:

not so special
very special

And this is indeed what I get with the Debug build configuration. However, with Release, I get this:

not so special
not so special

Which means the specialized implementation of special::call in special.cpp was ignored.

Why is the result inconsistent? What should I do to ensure that the specialized function is called in release builds?

like image 361
zneak Avatar asked May 06 '13 06:05

zneak


People also ask

Why not specialize function templates?

Moral #1: If you want to customize a function base template and want that customization to participate in overload resolution (or, to always be used in the case of exact match), make it a plain old function, not a specialization. And, if you do provide overloads, avoid also providing specializations.

What is function template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the specialty of a template function give example?

Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.


2 Answers

Your program has UB. An explicit specialisation or at least its declaration must be visible before being used. [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.

Add this declaration to special.h:

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

Alternatively, you can put the specialistation itself into the header. However, as a specialisation is no longer a template, it follows normal function rules and must be marked inline if placed in a header.

Also, be careful that function template specialisations have rather specific behaviour, and it's generally better to use overloads than specialisations. See Herb Sutter's article for details.

like image 150
Angew is no longer proud of SO Avatar answered Sep 20 '22 00:09

Angew is no longer proud of SO


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

In theory, the outcome can be anything. A compiler error, a crash, a silent online order for a pizza, anything. Even different behavior in debug and release compiles.

In practice, I guess the following happens: When linking the debug build, the Linker sees the same symbol defined twice in the two TUs, which is allowed only for templates and inline functions. Because of the ODR it may assume that both definitions are equivalent and picks the one from special.cpp, so you get by coincidence the behavior you expect.
During release build, the compiler inlines the call to special::call<string> during the compilation of main.cpp, so you get the only behavior seen in that TU: "not so special".

So how can you fix this?
In order to have only one definition for that specialization, you have to define it in one TU as you did, but you have to declare that there is a full specialization in any other TU, meaning declare that the specialization exists in the header special.h:

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

Or, which is seen more often, define it in the header, so it's seen in every TU. Since fully specialized function templates are normal functions, you will have to define it inline:

// in special.h
template<>
inline void special::call(const std::string&)
{ 
  puts("very special"); 
}
like image 33
Arne Mertz Avatar answered Sep 18 '22 00:09

Arne Mertz