Issue
I have the following code:
// template_header.hpp
#ifndef TEMPLATE_HEADER_HPP
#define TEMPLATE_HEADER_HPP
namespace template_header
{
template <int dim1>
/*static*/ constexpr int dim2 = 0;
template <>
/*static*/ constexpr int dim2<2> = 3;
template <>
/*static*/ constexpr int dim2<3> = 5;
}
#endif
// lib1.cpp
#include <array>
#include "template_header.hpp"
template <int dim1>
class lib1_class
{
public:
std::array< double, template_header::dim2<dim1> > ar1 = {0};
};
// lib2.cpp
#include <array>
#include "template_header.hpp"
template <int dim1>
class lib1_class
{
public:
std::array< double, template_header::dim2<dim1> > ar1 = {0};
};
If I compile any of the .cpp
files with static
uncommented, GCC gives me an "explicit template specialization cannot have a a storage class" error.
If static
is commented, I can compile both .cpp
files and then link them together as a shared library with g++ lib1.o lib2.o -shared -o shared_lib.so
.
However, if I compile with static
commented out with clang, I get no problems during compilation, but I get a "multiple definition of template_header::dim2<2>'" error during linking. If I uncomment
static`, then everything compiles and links fine.
I'm pretty confused about this, firstly given that this answer indicates that, since my constexpr
's happen in a namespace scope, they ought to automatically be static
and therefore should pose no problem for the linker even if static
is commented out.
Additionally, I don't understand why adding static
beforehand would change how GCC compiles the .cpp
files, given that it should be implicitly static.
Any explanation of the errors + possible fixes are appreciated.
Edit: I am using C++14.
Solution
So, without inline
variables, I was able to get something achieving your goals working. The basic idea is to have a "backend" struct
to hold static
members and then fully specialize that struct
to your options. This method has the added benefit of causing a compiler error if you attempt to access a member of the backend using a template parameter that has not been defined yet.
The header file would look like
#ifndef TEMPLATED_HEADER_HPP
#define TEMPLATED_HEADER_HPP
namespace template_header {
/**
* the "primary" template of the backend struct
* notice I leave the variable we want undefined!
* if you prefer to have a default for all other values of dim1
* you would put that here
*/
template<int dim1>
struct backend {};
template<>
struct backend<2> {
static constexpr int dim2 = 3;
};
template<>
struct backend<3> {
static constexpr int dim2 = 5;
};
/**
* Helper constexpr
* this is optional, but makes code outside this file more readable
* also, I named it in a way for your source files to be unchanged.
*/
template <int dim1>
constexpr dim2 = backend<dim1>::dim2;
}
#endif
I have a working version of this idea on Compiler Explorer which verifies that both this works with both GCC and clang. If you were to try to call dim2<dim1>
where dim1
does not equal 2 or 3, then a compiler error complaining about backend<dim1>::dims not defined
will be generated. You could add other members to the backend
specializations, taking care to keep the names the same across the different values of dim1
.
Total side note, why are you setting ar1 = {0};
? From my reading of the std array reference, this would only set the first element in the array to 0.
Answered By - Tom Eichlersmith