1

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,

  1. Enable only specific types of Pixel (mostly arithmetic)
  2. 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; 
Vuwox
  • 2,331
  • 18
  • 33
  • What part of your code is causing the error? – super Apr 06 '19 at 05:49
  • You are aware of: [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – πάντα ῥεῖ Apr 06 '19 at 07:25
  • @super On the list ctor this error happen when trying to call `: Base(list)`, and the error appear on under call but list is the first one in output due to call in main. And for piavta, I want to implement it in a cpp, first to limit the types that can be used and to speed up compilation. – Vuwox Apr 06 '19 at 14:08
  • 2
    @Vuwox I would suggest to make this a minimal example by removing all the pages of irrelevant code shown. You will increase your chances of solving the problem yourself greatly, and also the chances someone else will be able to help you. – super Apr 06 '19 at 15:56
  • Its kinda already a minimal example. I remove a lot of function from the working version. But I could try to get it a bit more clearer and smaller by just presenting the problem and not the old version also. – Vuwox Apr 06 '19 at 16:24
  • @Vuwox: You can still remove a great deal, probably including the multiple colors per `Pixel`, the operators, and the `initializer_list` constructors. – Davis Herring Apr 06 '19 at 17:22
  • @DavisHerring Done just now. And also provide the full compiler error. – Vuwox Apr 06 '19 at 17:27
  • After investigation, the message is pretty clear, I have a problem with the [order of initialization](https://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order), where the `Base` ctor is already called even before the `: Base()`, `: Base(val)` ... So by removing them and declaring the PixRGB ctor itself (aka in the `{}`), its working fine. I think I dont have really much more choice. But is there a way to use the Base class ctor anyway ? Im still searching – Vuwox Apr 06 '19 at 18:46
  • Your error message and code does not match. The error says you are trying to initialize `Pixel>` instead of `Pixel`. – super Apr 07 '19 at 08:33
  • The code shown compiles with [gcc](https://wandbox.org/permlink/ETg2pZg3KlqsRwkN). – super Apr 07 '19 at 08:41
  • I will give it a try, but I would like to have cross platform code and usually I compile with Intel compiler, but for test purpose I was in visual studio for now. I think the solution I have of a default ctor and each child defines their own ctor is enough for my need right now. No need to call parent and with this I run smoothly right now. – Vuwox Apr 07 '19 at 13:43

0 Answers0