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.
Related
I'm generating an constexpr std::array inside of a constexpr class, but I only want one instance of that class over all of the project where it is used.
I had originally made it a global, but then I found out that these globals are duplicated if I iterate over the array in multiple translation units. I then attempted to use a constexpr function, but I can't have a static inside of such a function. I'm using c++14, so I can't use an inline constexpr variable. An extern constexpr variable doesn't make sense because if you separate the declaration from the definition, then how can the constexpr values be dealt with in a compile time rather than a runtime manner when only one item is needed?
Are there any other options?
EDIT: See https://godbolt.org/z/5PcboYov4
Please remember that I'm not defining a regular variable. I'm defining a constexpr variable. The difference is IMPORTANT. It is being used in both non-constexpr and constexpr contexts.
Variable templates have external linkage, even if they are const-qualified:
template<typename = void>
constexpr std::array my_array{/*...*/};
// use my_array<> to access the array.
This is however only since DR 2387, which I guess should also apply to C++14 although it was resolved only in 2019.
If the compiler does not implement it, then you can add extern explicitly. As far as I can tell that is allowed and the declaration will still be a definition because it has an initializer. Because it is a template, there is no ODR violation for multiple definitions:
template<typename = void>
extern constexpr std::array my_array{/*...*/};
// use my_array<> to access the array.
For some reason I forgot the most obvious workaround using a static data member:
struct my_array_wrapper {
static constexpr std::array</*...*/> my_array{/*...*/};
};
// in one translation unit
constexpr std::array</*...*/> my_array_wrapper::my_array;
Then my_array_wrapper::my_array can be used. The definition in the translation unit is required if the array is ODR-used before C++17 only. Static data members in a class with external linkage also have external linkage, regardless of whether they are const-qualified.
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.
// 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;
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
I had to switch from g++-4.6 to 4.7 (so I can use some C++11 features). Now, the compiler complains:
In function WordJIT<float>::WordJIT(): undefined reference to JitRegType<float>::Val_t
I am wondering if something has changed between these compiler version that can affect symbol resolving. Or is the language better implemented in the new version (4.7) and what I am doing is wrong: (same code compiles with 4.6)
jit.h
class Jit {
public:
enum RegType { f32=0,f64=1,u16=2,u32=3,u64=4,s16=5,s32=6,s64=7 };
// ...
};
template <class T> struct JitRegType {};
template <> struct JitRegType<float> { static const Jit::RegType Val_t = Jit::f32; };
wordjit.h
#include "jit.h"
template<class T>
class WordJIT
{
WordJIT() {
mapReg.insert( std::make_pair( JitRegType<T>::Val_t , jit.getRegs( JitRegType<T>::Val_t , 1 ) ) );
}
private:
typedef std::map< Jit::RegType , int > MapRegType;
mutable MapRegType mapReg;
};
Edit:
Is static const okay in a header file or should one use constexpr ?
Is there a way to declare also Val_t in the class declaration of JitRegType but not actually define it ?
Per 9.4.2p3:
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer [...] The member shall still be defined in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
So you need to add to your program (probably in jit.cpp):
const Jit::RegType JitRegType<float>::Val_t;
This is so that if the static const member is used in a context where a reference to it is required, there exists a unique definition for the linker to refer to (the same as any static member that is not a member of a class template or class template partial specialization).
The issue is discussed in depth on the gcc wiki.
Note that both gcc 4.6 and 4.7 are behaving sensibly; it is just that gcc 4.6 chooses to inline the value of JitRegType<float>::Val_t where gcc 4.7 chooses not to (or possibly is inlining it but emitting a linker reference to the definition as well). It's a little difficult to tell whether the implementation is required to issue a diagnostic; 9.4.2p3 describes a diagnosable rule but then 9.4.2p4 (implicitly referring to non-const static data members) says that a diagnostic is not required. Either way, as a quality of implementation issue it's better that a compiler issue a diagnostic than not.