Reputation: 41
I'm reading Meyers' book on modern c++, where I find a code snippet might be useful:
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept {
return N;
}
This function deduces N
for us as a compile-time constant. So I want to apply it in my code:
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept {
return N;
}
template <typename T>
class A {
public:
const static char* names[];
};
template<typename T>
const char* A<T>::names[] = {"foo", "bar"};
template<>
const char* A<bool>::names[] = {"foo","bar", "foobar"};
If put in one file it works perfectly fine, arraySize(A<int>::names)
is 2
and arraySize(A<bool>::names)
is 3
.
But when used in larger project requiring separate .h
and .cpp
, the problem comes:
If put the declaration of the specified version of A<bool>::names[]
in a .cpp
, the code compiles(and links) but the compiler can't see it when deducing arraySize()
, so arraySize(A<bool>::names)
is deduced to 2
.
If put the declaration of A<bool>::names[]
in a .h
, of course, we get a "duplicate symbol" link error.
So how can I make arraySize(A<bool>::names)
correctly deduced to 3
?
Upvotes: 4
Views: 1173
Reputation: 66200
You're using a constexpr
functions so you're using a C++11 (or newer) compiler.
So, if your class A
contains only names
(otherwise you can create a base class only for names
), you can declare it static constexpr
and (specializing the class) you can intialize it in the body class (in the header) and define it outside the body (in a cpp file, if you need) without initializiang it.
And, if I uderstand correctly, starting from C++17, the outside of the class definition is no more necessary.
The following is an example
#include <iostream>
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept {
return N;
}
template <typename T>
class A {
public:
static constexpr char const * names[] = {"foo", "bar"};
};
template <>
class A<bool> {
public:
static constexpr char const * names[] = {"foo", "bar", "foobar"};
};
template<typename T>
constexpr char const * A<T>::names[];
constexpr char const * A<bool>::names[];
int main()
{
std::cout << arraySize(A<long>::names) << std::endl; // print 2
std::cout << arraySize(A<bool>::names) << std::endl; // print 3
}
--- EDIT ---
The OP write
It is elegant for classes with one member. But my class contains other members and methods so I'll go for "complete with the dimension" one in question's comments, for that requires minimal modification of my original code
I add a modified example where names
is inserted in a simple template base struct (namesB
, base-for-names), containing only names
.
This permit the specialization only for the simple namesB
and a development, only one time, for a complex class A
.
#include <iostream>
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept
{ return N; }
template <typename T>
struct namesB
{ static constexpr char const * names[] = {"foo", "bar"}; };
template <>
struct namesB<bool>
{ static constexpr char const * names[] = {"foo", "bar", "foobar"}; };
template <typename T>
class A : public namesB<T>
{ /* a complex class defined only one time */ };
template<typename T>
constexpr char const * namesB<T>::names[];
constexpr char const * namesB<bool>::names[];
int main()
{
std::cout << arraySize(A<long>::names) << std::endl; // print 2
std::cout << arraySize(A<bool>::names) << std::endl; // print 3
}
Upvotes: 1
Reputation: 79
You can't have class templates use a .cpp file, it simply doesn't work with how compilers work. What the compiler does is make a version for every time you send a new data type to the class.
So basically if you have a class template that takes one variable, and you make two object, one with an int, and one with a string, of the class type, the compiler will make two definitions of the class. The problem is that the compiler doesn't know to do this with the .cpp also. I heard that you can rename the .cpp to anything else, like .tpp, or manually overload the class definition. Both ways are kinda hacky, and everyone just writes template classes in the .h file, which might seem none uniform from other classes, but it is just how it is.
Upvotes: 0