Goal
I want to implement a struct Derived<size_t ...Tpar>, satisfying certain requirements. I have a functioning solution code, but it has certain disadvantages. I seek a better way of doing it.
Requirements
Derived<size_t ...Tpar> must inherit exclusively from Base<size_t ...Tpar>.
The relevant cases for me are Derived<size_t> and Derived<>.
Members and attributes:
- an identical member function
void a(void);for empty and non-empty Tpar - an array
dataof lengthTpar[0]forDerived<Tpar...>for non-empty Tpar only - and a function
void b(void);for non-empty Tpar only, that performs computations ondata.
Minimum working example
As the comments in the example code below indicate, these issues make the solution unappealing:
- Since
Derived<>andDerived<size_t>both implementa(), I thought of inheritingDerived<size_t>fromDerived<>. But this does not work becauseDerived<size_t>would inherit fromBase<>, which is against requirement. - I hence opted for a specialized Submodule. But this does not work because Derived is already a template, hence I cannot specialize a submodule template within its namespace as per the rules of the language.
- I thus make the Submodule in two non-template explicit variants and employ
conditional_tto obtain the right type of submodule in eitherDerivedspecialization. 3.1. Is there a better alternative for the array (that I have to augment with a zero only for the conditional to not complain thataTpar[0]is a null-pointer? 3.2. Is there a way for templating the submodule or making the entire code shorter, without exposing the Submodule to the compilation unit?
#include<iostream>
#include<array>
#include<type_traits>
template<size_t ...Tpar> class Base{};
// I cannot inherit Derived<Tpar> from Derived<> since this would make Derived<Tpar> a derived of Base<>.
template<size_t ...Tpar>
class Derived: Base<Tpar...>{
// c++ won't allow template structure specializations within a template struct
template<size_t n>
struct SubModule_paged{
double data[n];
void foo(){
// foo is a routine that computes on data.
std::cout << "Sub<"<<n<<">foo.\n";
}
};
struct SubModule_empty{
// no data, hence no foo.
void foo() = delete; // COMPILER HINT: Intentionally, Derived<> has no member b.
};
static constexpr size_t nTpar = sizeof...(Tpar);
static constexpr std::array<const size_t, nTpar+1> aTpar = {Tpar...,0}; // <-- This is ugly!
using SubModuleType = std::conditional_t< (nTpar>0) , SubModule_paged<aTpar[0]> , SubModule_empty >;
//
SubModuleType sub;
//
public:
void a(){
std::cout << "a.\n";
}
void b(){
std::cout << "b.\n";
sub.foo();
}
};
int main(){
Derived< > x;
Derived<3> y;
x.a();
//x.b();
y.a();
y.b();
}
Ok,
ashould be member ofBase.Ok, specialize
Derivedfor whenTpar...is at least one argument.Maybe you will not agree about making
aa member ofBase, but the thing about distinguising between empty and non emptyTpar...is actually rather simple. You only need to consider thatsize_t Tfirst,size_t ...Tparis one or more argument, whilesize_t Tpar...is zero or more. I think you got confused by thinking about the parameter pack like an array where you need to pick the first element viaTpar[0]. But a parameter pack is not an array and no array is needed to pick the first element either.