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-emptyTparonly and a function voidb(void);for non-emptyTparonly, 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.