EDIT: As suggested, I reformat all the question to be much shorter and straight to the point, if you want all the code, check the edit.
Im trying to explicitly instantiate a CRTP template in a .cpp for 2 reasons,
- Enable only specific types of Pixel (mostly arithmetic)
- Speed up compilation time using a .cpp
But by using the following, I have problem with the explicit instantiation at the end of the .cpp, here the code:
Pixel.h
#include <cstdio> // std::size_t
#include <initializer_list> // std::initializer_list
template<typename T, std::size_t N, template<typename> class Derived>
struct Pixel
{
// Friend the derived class to allow it to use current ctor.
friend Derived<T>;
public:
T ch[N];
void print() const;
// Ctor are protected to avoid instantiation of Pixel object
// but they are needed to create the array object at the end.
protected:
Pixel();
Pixel(T val);
Pixel(const std::initializer_list<T>& list);
};
template<typename T>
struct PixRGB : public Pixel<T, 3, PixRGB>
{
// Create an alias for an easy accessor over the base class.
using Base = Pixel<T, 3, PixRGB>;
PixRGB();
PixRGB(T val);
PixRGB(const std::initializer_list<T>& list);
};
Pixel.cpp
#include "Pixel.h"
#include <algorithm> // std::copy
#include <iostream> // std::cout
#include <string> // std::to_string
template<typename T, std::size_t N, template<typename> class Derived>
Pixel<T, N, Derived>::Pixel() {}
template<typename T, std::size_t N, template<typename> class Derived>
Pixel<T, N, Derived>::Pixel(T val) { for (int i = 0; i < N; i++) ch[i] = val; }
template<typename T, std::size_t N, template<typename> class Derived>
Pixel<T, N, Derived>::Pixel(const std::initializer_list<T> & list)
{ std::copy(list.begin(), list.end(), std::begin(ch)); }
template<typename T, std::size_t N, template<typename> class Derived>
void Pixel<T, N, Derived>::print() const {
for (int i = 0; i < N; i++)
std::cout << std::to_string(ch[i]) << " ";
}
// PixRGB
template<typename T>
PixRGB<T>::PixRGB() : Base() {}
template<typename T>
PixRGB<T>::PixRGB(T val) : Base(val) {}
template<typename T>
PixRGB<T>::PixRGB(const std::initializer_list<T> & list) : Pixel<T, 3, PixRGB>(list) {}
// Explicit instantiation
template struct PixRGB<float>;
template struct Pixel<float, 3, PixRGB>;
Main.cpp
#include <iostream>
#include "Pixel.h"
int main()
{
std::cout << "Sizeof PixRGB<float> : " << sizeof(PixRGB<float>) << std::endl;
PixRGB<float> a = { 1.0f, 2.0f, 3.0f };
a.print();
return 0;
}
The compilation error
Pixel.cpp(42): error C2614: 'PixRGB<float>': illegal member initialization: 'Pixel<float,3,PixRGB<float> >' is not a base or member
Pixel.cpp(42): note: while compiling class template member function 'PixRGB<float>::PixRGB(const std::initializer_list<T> &)'
with
[
T=float
]
Pixel.cpp(45): note: see reference to class template instantiation 'PixRGB<float>' being compiled
Pixel.cpp(41): error C2437: 'Base': has already been initialized
Pixel.cpp(41): note: while compiling class template member function 'PixRGB<float>::PixRGB(T)'
with
[
T=float
]
Pixel.cpp(40): error C2437: 'Base': has already been initialized
Pixel.cpp(40): note: while compiling class template member function 'PixRGB<float>::PixRGB(void)'
Note that I tried to change Base(list)
to Pixel<T,3,PixRGB>(list)
for testing purpose, and the error message change (as shown above), if I put back Base(list)
, the error is similar to other two ctors.
The above compile/execute well if I change the template of Pixel to be:
template<typename T, std::size_t N, class Derived>
But I need to know typename such as Derived to support function as this one:
template<typename T2>
Derived<T2> convert() const;