// i.h
template<int> extern int const i;
// i.cpp
#include "i.h"
template<> extern int constexpr i<0> = 42;
// main.cpp
#include "i.h"
int main()
{
return i<0>;
}
In C++14/17 mode this returns 42 with clang, but is an error with gcc: "explicit template specialization cannot have a storage class".
Is this a bug in gcc?
There is a rather simple solution to this whole issue. Please additionally see this post on the ISO C++ Standard - Discussion forum and the reply from Richard Smith.
1.
extern must not be specified in an explicit specialization
So to answer the original question: no, it is not a bug in gcc, it is correct to report an error (as Massimiliano Janes already answered).
In contrast clang actually has a bug here (as Massimiliano Janes already guessed) because extern is accepted. Maybe clang accepts it silently because it is the same as that of the primary template.
2.
Theoretically (according to the standard) the solution is to drop extern because with templates linkage is by name and so the specialization 'inherits' the linkage of the primary template (again see Massimiliano Janes' answer)
But in practice it does not work because both compilers are incorrect here and the explicit specialization incorrectly has internal linkage instead of the linkage of the primary template which is external.
3.
In summary:
gcc never compiles which is correct in (1) but incorrect in (2).
clang compiles in (1) which is incorrect but does not compile in (2) which is also incorrect.
I'll file a bug report for clang. If someone is interested please feel free to file a bug for gcc. I won't do this because (unfortunately) I can't use gcc in my development environment Visual Studio.
The primary variable template must be declared extern since it is const and I don't want initializers in the header file (just like with ordinary "extern int const i;"). Instead I want specialization definitions in some source file.
the solution should be to drop the 'extern' in the specialization.
because
[declarations/specifiers-7.1.1]A storage-class-specifier shall not be specified in an explicit specialization
the rationale being that all specializations should have the same linkage ( see for example defect report 605). So, it seems clang's wrong here.
Anyway, given that compilers turn out behaving wildly on this, a workaround could be something like
// i.h
template<int I> struct i_impl{ static const int value; };
template<int I> int const i = i_impl<I>::value;
// i.cpp
#include <i.h>
template<> const int i_impl<0>::value = 42;
According to N4340:
A storage-class-specifier othe than thread_local shall not be
specified in an explicit specialization (14.7.3) or an explicit
instantiation (14.7.2) directive.
So, This happens with the extern specifier. Just remove any storage specifier on the specialization.In your code remove extern specifier. Such as :
template<>
int constexpr i<0> = 42;
Related
The following code (taken from Wikipedia) defines the variable template pi<>:
template<typename T=double>
constexpr T pi = T(3.14159265358979323846264338328);
template<>
constexpr const char* pi<const char*> = "π";
With the clang compiler (Apple clang version 12.0.0) (with C++14), this triggers a warning (with -Weverything):
no previous extern declaration for non-static variable 'pi<const char *>'
declare 'static' if the variable is not intended to be used outside of this translation unit
Moreover, since this was defined in a header, multiple instances of 'myNameSpace::pi<char const*>' were created, causing linker errors.
So, as suggested, I added the static keyword, which silenced the warning:
template<>
static constexpr const char* pi<const char*> = "π";
But now gcc (9.3.0) is unhappy, giving an error pointing at the static keyword:
error: explicit template specialization cannot have a storage class
What is the correct way to avoid either warning and error?
The warning from (this old version of) Clang is partly misleading, but does indicate the real problem that you eventually encountered with the linker. The warning describes the good rule of thumb that a global variable ought to
appear with extern in a header and then without in a source file, or
appear with static in a source file (avoiding collisions with any other symbol).
The latter choice doesn't apply to explicit specializations: since linkage applies to templates as a whole (the standard says that it pertains to the name of the template, which is evocative even if it doesn't work well for overloaded functions), you can't make just one specialization static and Clang is incorrect to accept it. (MSVC also incorrectly accepts this.) The only way to make a "file-local specialization" is to use a template argument that is a local type, template, or object. You can of course make the whole variable template have internal linkage with static or an unnamed namespace.
However, the former choice does apply: an explicit specialization is not a template, so it must be defined exactly once (in a source file). Like any other global variable, you use extern to reduce the definition to a declaration:
// pi.hh (excerpt)
template<typename T=double>
constexpr T pi = T(3.14159265358979323846264338328);
template<>
extern constexpr const char* pi<const char*>;
// pi.cc
#include"pi.hh"
template<>
constexpr const char* pi<const char*> = "π";
(Since the primary template is, well, a template, it is defined in the header file.)
As mentioned in the comments, C++17 allows inline variables; your explicit specialization again behaves like an ordinary global variable and can be defined with inline in a header if desired.
Consider the following code:
template <typename T> int foo();
template <typename T> int foo() = delete;
is this valid C++11?
GCC (9.1) says: Yes!
clang (8.0) says: No!
nvcc (9.2) says: No!
MSVC (19.20) says: Yes! (in C++14 mode, it doesn't support C++11.)
... see it all on GodBolt.
so which compilers are right and which compilers are s##$%e ? :-)
GCC and MSVC have a bug.
[dcl.fct.def.delete]
4 ... A deleted definition of a function shall be the first declaration of the function or, for an explicit specialization of a function template, the first declaration of that specialization...
Which I believe stands for instantiated declarations and definitions too. Since referring to a deleted function is a hard error, it must be declared as deleted asap.
I am getting the error in while linking my object files:
#include <cstdint>
#include <array>
enum SystemType : uint8_t { AC, DC, HCP, EFF };
template<SystemType TYPE> struct System;
template<>
struct System<AC> {
public:
static constexpr size_t number_of_sockets = 2;
static constexpr std::array<size_t, number_of_sockets> object_per_socket { { 12, 6 } };
};
I am using it as below to allocate data into a vector.
terminal->no_obj_per_system.assign(
Sytem<AC>::object_per_socket.begin(),
Sytem<AC>::object_per_socket.end());
I am using clang on mac Os.
In C++14 and earlier, static data members must have an out-of-class definition in exactly one translation unit if they are odr-used; this rule still applies to constexpr members.
This question contains an example with references from the C++14 Standard.
If there is no out-of-class definition provided, and the variable is odr-used, then it is ill-formed with no diagnostic required, which explains why some people don't get a compilation error. To be on the safe side you should provide a definition, there's no harm doing so even in the case where the variable is not odr-used.
The definition would look like, (NOT in a header file):
constexpr std::array<size_t, System<AC>::number_of_sockets> System<AC>::object_per_socket;
In C++17 there is a new feature "inline variables", which allows variables to be defined in header files with similar semantics to inline functions, i.e. you're allowed to have multiple matching definitions across translation units, and the compiler/linker will select one as needed. constexpr variables will be implicitly inline, so your original code will be correct in C++17. Recent versions of gcc and clang should accept the code with -std=c++1z flag.
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(); }
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