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.
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:
void a(void);
for empty and non-empty Tpardata
of length Tpar[0]
for Derived<Tpar...>
for non-empty Tpar onlyvoid b(void);
for non-empty Tpar only, that performs computations on data
.As the comments in the example code below indicate, these issues make the solution unappealing:
Derived<>
and Derived<size_t>
both implement a()
, I thought of inheriting Derived<size_t>
from Derived<>
. But this does not work because Derived<size_t>
would inherit from Base<>
, which is against requirement.conditional_t
to obtain the right type of submodule in either Derived
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 that aTpar[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();
}
an identical member function
void a(void);
for empty and non-emptyTpar
Ok, a
should be member of Base
.
an array data of length
Tpar[0]
forDerived<Tpar...>
for non-emptyTpar
only and a function voidb(void);
for non-emptyTpar
only, that performs computations on data.
Ok, specialize Derived
for when Tpar...
is at least one argument.
#include <iostream>
#include <array>
template<size_t ...Tpar> struct Base{
void a(void) {}
};
// base template
template <size_t ...Tpar> struct Derived : public Base<Tpar...> {};
// specialization for one or more size_t
template <size_t Tfirst,size_t ...Tpar> struct Derived<Tfirst,Tpar...> : public Base<Tfirst,Tpar...> {
std::array<size_t,Tfirst> arr;
void b() {}
};
int main() {
Derived<> d;
//d.b(); error
//d.arr; error
Derived<42> e;
e.b();
e.arr;
}
Maybe you will not agree about making a
a member of Base
, but the thing about distinguising between empty and non empty Tpar...
is actually rather simple. You only need to consider that size_t Tfirst,size_t ...Tpar
is one or more argument, while size_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 via Tpar[0]
. But a parameter pack is not an array and no array is needed to pick the first element either.