Template specialization for static function when compiling with g++ - c++

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.

Related

Are variable templates declared in a header, an ODR violation?

What happens when a header file contains a template variable like the following:
template <class T>
std::map<T, std::string> errorCodes = /* some initialization logic */;
Is this variable safe to use? Doing some research on this I found that:
Templates are implicitly extern but that does cause ODR violations
Without any static or inilne or constexpr decoration I can end up with multiple definitions
Keyword inline of C++17 changes the rules but c++20 has a different behavior(?)
So which one is it?
Templates get an exception from the one-definition rule, [basic.def.odr]/13:
There can be more than one definition of a [...] templated entity ([temp.pre]) [...] in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements.
There's a bunch of requirements there, but basically if you have a variable template in a header (a variable template is a kind of templated entity) and just include that header from multiple translation units, they will have the same token-for-token identical definition (because #include), so having more than one definition is fine.
This is the same reason that you don't need to declare function templates inline in headers, but do need to declare regular functions inline.
In C++17, this wording read:
There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements.
Note that variable template is not in that list, which was just an omission (it was very much always intended to work). This was CWG 2433, adopted in 2019, but as a defect report (DR) so I wouldn't consider it as counting as a C++20 change. This is the DR that introduced the "templated entity" bullet I cited earlier (rather than listing out several different kinds of templated entity manually, and missing one).
Is this variable safe to use?
Yes you can declare variable templates this way in headers and then use them in other translation units. One example is given below:
header.hpp
#ifndef HEADER_H
#define HEADER_H
#include <map>
template <class T>
std::map<T, std::string> errorCodes{}; //zero initialized value. Also, note there is no need to use keyword inline here
#endif
print.hpp
#ifndef PRINT_H
#define PRINT_H
void print();
#endif
print.cpp
#include "print.hpp"
#include "header.hpp"
#include <iostream>
void print()
{
std::cout << errorCodes<int>.at(15) << std::endl; // This is safe: prints SomeString
}
main.cpp
#include <iostream>
#include "print.hpp"
#include "header.hpp"
int main()
{
errorCodes<int>[15] = "SomeString"; //This is safe
print();
}
Note in the above program you don't need to use the keyword inline in header.hpp while defining the variable template errorCodes. The C++ compilation system will take care of that.
The output of the above program can be seen here.

Why do compiler options impact selection of template implementation?

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.

Is it safe to place definition of specialization of template member function (withOUT default body) in source file?

Here's what I mean:
// test.h
class cls
{
public:
template< typename T >
void f( T t );
};
-
// test.cpp
template<>
void cls::f( const char* )
{
}
-
// main.cpp
int main()
{
cls c;
double x = .0;
c.f( x ); // gives EXPECTED undefined reference (linker error)
const char* asd = "ads";
c.f( asd ); // works as expected, NO errors
return 0;
}
This is completely fine, right?
I started doubting this, because I just ran over the specialization of '...' after instantiation error, which was new to me. So, I "worked around" this error and everything seems to work fine now, but still..
Is this well-defined behavior?
edit: And the same for non-member template functions (forward declared non-member template functions).
Lightness Races in Orbit cited why it's not compliant parts from the Standard. There might be some others, in the vicinity.
I will try to explain in simpler terms what the Standard verbiage means, and hopefully I'll get it correctly, and finally explain the linker errors (or absence of error):
What is the point of instantiation ?
How does the compiler select a specialization ?
What is necessary at the point of instantiation ?
Why a linker error ?
1/ What is the point of instantiation ?
The point of instantiation of a template function is the point where it is called or referred to (&std::sort<Iterator>) with all the template parameters fleshed out (*).
template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }
int main() { foo(1); } // point of instantiation of "foo<int>(int)"
It can be delayed though, and thus not match the exact call site, for templates called from other templates:
template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }
template <typename T>
void bar(T t) { foo(t); } // not a point of instantiation, T is still "abstract"
int main() { foo(1); } // point of instantiation of "bar<int>(int)"
// and ALSO of "foo<int>(int)"
This delay is very important as it enables writing:
co-recursive templates (ie, templates that refer to each-others)
user-specializations
(*) Roughly speaking, there are exceptions such as non-template methods of a template class...
2/ How does the compiler select a specialization ?
At the point of instantiation, a compiler need to be able to:
decide which base template function to call
and possibly, which of its specializations to call
This old GotW shows off the woes of specializations... but in short:
template <typename T> void foo(T); // 1
template <typename T> void foo(T*); // 2
are overloads, and each spawns a distinct family of possible specializations of which they are the base.
template <> void foo<int>(int);
is a specialization of 1, and
template <> void foo<int*>(int*);
is a specialization of 2.
In order to resolve the function call, the compiler will first pick the best overload, while ignoring template specializations, and then, if it picked a template function, check if it has any specialization that could better apply.
3/ What is necessary at the point of instantiation ?
So, from the way a compiler resolve the call, we understand why the Standard specifies that any specialization should be declared before its first point of instantiation. Otherwise, it simply would not be considered.
Thus, at the point of instantiation, one needs to have already seen:
a declaration of the base template function to be used
a declaration of the specialization to be selected, if any
But what of the definition ?
It is not needed. The compiler assumes it will either be provided later on in the TU or by another TU entirely.
Note: it does burden the compiler because it means it needs to remember all the implicit instantiations it encountered and for which it could not emit a function-body so that when it finally encounters the definition it can (at last) emit all the necessary code fo all the specializations it encountered. I wonder why this particular approach was selected, and also wonder why even in the absence of an extern declaration the TU may end with undefined function-bodies.
4/ Why a linker error ?
Since no definition is provided, gcc trusts you to provide it later and simply emits a call to an unresolved symbol. If you happen to link with another TU that provides this symbol, then everything will be fine, and otherwise you'll get a linker error.
Since gcc follows the Itanium ABI we can simply look up how it mangles the symbols. It turns out that the ABI makes no difference in mangling specializations and implicit instantiations thus
cls.f( asd );
calls _ZN3cls1fIPKcEEvT_ (which demangles as void cls::f<char const*>(char const*)) and the specialization:
template<>
void cls::f( const char* )
{
}
also produces _ZN3cls1fIPKcEEvT_.
Note: it is not clear to me whether an explicit specialization could have been given a different mangling.
No, I don't think it's okay:
[C++11: 14/6]: A function template, member function of a class template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated (14.7.1) unless the corresponding specialization is explicitly instantiated (14.7.2) in some translation unit; no diagnostic is required.
[C++11: 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. [..]
Frankly I can't explain why it works for you.
I think that your original code was incorrect and your "workaround" is not standard-compliant, too (despite the fact that your compiler and linker process it). Good quotes from the standard were cited in the answer of #Lightness Races in Orbit. See also the following example from the standard ([temp.expl.spec] 14.7.3/6):
class String { };
template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }
void f(Array<String>& v) {
sort(v); // use primary template
// sort(Array<T>&), T is String
}
template<> void sort<String>(Array<String>& v); // error: specialization
// after use of primary template
template<> void sort<>(Array<char*>& v); // OK: sort<char*> not yet used
I marked my answer as community wiki because in fact it is only a big comment.

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.