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
data
of 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_t
to obtain the right type of submodule in eitherDerived
specialization. 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,
a
should be member ofBase
.Ok, specialize
Derived
for whenTpar...
is at least one argument.Maybe you will not agree about making
a
a 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 ...Tpar
is 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.