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

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.

Related

Where should the definition of an explicit specialization of a class template be placed in C++?

According to [temp.spec]/5:
For a given template and a given set of template-arguments,
...
an explicit specialization shall be defined at most once in a program (according to [basic.def.odr]), and
...
the definition of an explicit (full) specialization of a class template cannot be placed in a header (otherwise there is one definition in each translation unit containing this header, thus there will be more than one definition in the whole program).
In addition, as another evidence, the entities listed in [basic.def.odr]/12 (blockquoted below) do not contain a full specialization of a class template. Instead, "template specialization for which some template parameters are not specified" is contained.
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, concept ([temp.concept]), 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.
However, if I place the definition in a source file and leave its declaration in a header, for example,
// "a.h"
template <typename T>
struct S {};
template <>
struct S<int>; // declaration
// "a.cpp"
#include "a.h"
template <>
struct S<int> {}; // definition
// "main.cpp"
#include "a.h"
int main()
{
S<int> s;
}
then an error occurs (tested by gcc) because S<int> is an incomplete type.
In conclusion, where should I place the definition of an explicit specialization of a class template?
I am going to try to summarize here what I've learned through the discussion in my other answer, in the hopes of leaving a good answer to this question, rather than having the answer be buried in the comments.
The standard says
an explicit specialization shall be defined at most once in a program (according to ODR)
ODR is the One Definition Rule. You can only define each class once within a program, with an exception designed to allow a class definition to be available in each translation unit: you can define a class in different translation units as long as these different definitions are identical, character for character. The quote of the OP is part of the ODR description, follow the OP's link to see the full description.
So IMO the standard's text above means that an explicit specialization can be defined only once, but according to the ODR, and thus with the same exceptions: you can define it in a header file so it is available in multiple translation units.
Note that it is not possible to instantiate a class without its full definition (the compiler needs to know at least how many bytes to allocate for it). The same is true for a templated class, or a specialization of such a class. So it must be possible for the definition to be present in each translation unit that uses it.
This is a definition (the specialization will be instantiated), not a declaration, it probably ought to go in a specific source file (*.cpp):
template <> struct S<int>;
Note: it doesn't "hurt" to have this in every translation unit... other than the time it takes to instantiate by the compiler, and the bloat for the object files (*.o or *.obj).
This is a declaration (the specialization will not be instantiated), and is okay to put in a header file (*.h):
extern template <> struct S<int>;
The declaration requires C++11 or later.

Resolving Definitions of Specialized Static Member Variables of Templated Classes

Compiler Fights XIV: Doom of the Duplicitous Double Definition, co-starring The Dubious Declaration!
Compilers, all with either -O0 or Debug mode:
g++ 5.2.0
clang++ 3.6.0
VC++ 18.00.40629 (MSVC 2013, Update 5)
Summary:
Is VC++ wrong in rejecting the declaration and definition of a specialized static member variable of a templated class with the syntax?
template <> const std::string TemplatedClass::x; // .h file
template <> const std::string TemplatedClass::x= "string"; // .cpp file
Does removing the declaration in the header file cause an otherwise well-defined program to be ill-formed?
If so, is there a VC++ friendly way to declare the specialization of a static member variable of a templated class?
While making an MCVE of a problem I was having with defining specialized static member variables of a template, I encountered an interesting variation in behavior between VC++, GCC and Clang with respect to the declaration said specialized static member variables. Specifically, the syntax
template <> const std::string TemplatedClass<int>::x; // .h file
template <> const std::string TemplatedClass<int>::x= "string"; // .cpp file
seems to mortally offend VC++, which responds with complaints of multiple definitions:
error C2374: 'member' : redefinition; multiple initialization
while both gcc and clang take this in stride.
Research
I'm assuming the latter two are correct because they usually are, and also because the above syntax is from an answer regarding static member initialization of a specialized template class, which quotes paragraph 14.7.3/15 from the standard of 2010 in stating that template<> X Q<int>::x is a declaration, not a definition. I took the liberty of tracking down the equivalent paragraph of draft N4296, thinking it could have changed in the intervening time. It has, but only in that it's moved two paragraphs up and contains additional clarification:
14.7.3/13
An explicit specialization of a static data member of a template or an explicit specialization of a static data member template is a definition if the declaration includes an initializer; otherwise, it is a declaration.
[ Note: The definition of a static data member of a template that requires default initialization must use a braced-init-list:
template<> X Q<int>::x; // declaration
template<> X Q<int>::x (); // error: declares a function
template<> X Q<int>::x { }; // definition
— end note ]
This seems pretty clear to me, but VC++ seems to have a different interpretation. I've tried simply commenting out the offending declaration, and no compilers complain, which would seem to solve my troubles, but doesn't because paragraph 6 has this to say: (worrying emphasis mine)
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.
It provides examples, but all of them are for specializing functions after they're used or specializing member enums and classes of a templated type, which I'm fairly certain don't apply to this problem. However, the initial words of p13 seem to imply that the declaration of the specialized static member variable is also an explicit specialization, at least when using the illustrated syntax.
MCVE
The test I used for my experimentation can be found on Coliru, with apologies to StackedCrooked for the fairly involved command line. A much shortened version is below:
main.cpp
#include <iostream>
// 'header' file
#include "test.h"
int main(){
std::cout << test::FruitNames<Fruit::APPLE>::lowercaseName();
}
test.h (declaration not commented out)
test.h (declaration commented out)
#ifndef TEMPLATE_TEST
#define TEMPLATE_TEST
#include <algorithm>
#include <locale>
#include <string>
namespace test{
enum class Fruits{
APPLE
};
template <Fruits FruitType_>
class FruitNames{
static const std::string name_;
/*...*/
public:
static std::string lowercaseName() {/*...uses name_...*/}
};
// should be counted as declaration. VC++ doesn't.
template <> const std::string FruitNames<Fruits::APPLE>::name_;
} // end namespace test
#endif // TEMPLATE_TEST
test.cpp
#include "test.h"
namespace test{
template <> const std::string FruitNames<Fruits::APPLE>::name_ = "Apple";
}
Output
Both gcc and clang will output
apple
with or without the specialization declaration in test.h. VC++ will do so if the declaration in test.h is commented out, but will produce a double initialization error if it is present.
Finally
Is VC++ incorrect to reject the declaration/explicit specialization syntax for the static member variable of a templated class as previously stated, or is it an allowed but not mandatory diagnostic error?
Does the removal of the declaration cause the program to be
ill-formed?
If it is ill formed without the declaration, how do I get VC++ to play nice with a
well-defined program?
Is VC++ incorrect to reject the declaration/explicit specialization syntax for the static member variable of a templated class as previously stated, or is it an allowed but not mandatory diagnostic error?
Yes, this is a bug in VC++. It has apparently been fixed in Visual Studio 2019 version 16.5 Preview 2.
Does the removal of the declaration cause the program to be ill-formed?
Your quote from the standard seems to suggest that. Other people agree.
If it is ill formed without the declaration, how do I get VC++ to play nice with a well-defined program?
As a workaround, you can specialize the whole class and then define the member without the template<> syntax. See Amir Kirsh's answer to a similar question:
https://stackoverflow.com/a/58583521/758345
Alternatively, you could define and initialize your variable in your header and mark it as inline (since c++17):
template <> inline const std::string TemplatedClass::x = "string"; // .h file

Class template member specialization

I'm specializing member functions of a template class in a header file like so:
#pragma once
#include <iostream>
template<class T>
struct Test
{
void Print() { }
};
template<>
void Test<int>::Print()
{
std::cout << "int" << std::endl;
}
Is it correct to put the specialization in a header file (without it being inline), or should it be in a cpp file? It compiles fine as shown above (using VS2012), but I'm rather surprised I don't get multiple definition linker errors.
The ODR requires exactly one definition for non-inline functions that are ODR-used (that roughy means, for functions, being potentially called).
Quoting n3485, [basic.def.odr]
4 Every program shall contain exactly one definition of
every non-inline function or variable that is odr-used in that
program; no diagnostic required.
Then, there's an exception for templates (i.e. not for functions):
6 There can be more than one definition of a class type [...], 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 in a program provided that [...]
[emphasis mine]
An explicit specialization of a template is not a template. For example, an explicitly specialized class template is a class (with a strange name). Therefore, your assumption is correct and multiple definitions for explicitly specialized members of class templates violate the ODR.
With g++4.8.1, I even get a linker error in such a program; note that I have ODR-used the function. No diagnostic is required for a violation of the ODR.
Putting the specialisation in the header file is the canonical form (as boost does), it doesn't violate the ODR.

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.