Is it possible to have a template base class and a non-template derived class?
Yes, see AAA
, BBB
, CCC
in the example below.
If so, what if the base class is a non-type template?
See DDD
, EEE
, FFF
in the example below.
In C++20 you can have some kind of string-literal template arguments.
This page gives a concise example about their usage.
I'm not certain about your intention, and what is supposed to be constexpr
or dynamic in your use case, but here is an attempt.
Some properties of the string-literals are checked at compile time thanks to static_assert()
.
/**
g++ -std=c++20 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <iostream>
#include <algorithm> // std::copy_n()
template<typename T> class AAA { public: T member; };
class BBB: public AAA<int> { };
class CCC: public AAA<double> { };
template<int N> class DDD { public: static constexpr int value=N; };
class EEE: public DDD<100> { };
class FFF: public DDD<1000> { };
template<size_t N>
struct StringLiteral
{
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
char value[N];
};
constexpr bool someFunction(const char *n) { return n[0]<n[1]; }
template<StringLiteral name>
class Base
{
public:
static constexpr const char* base_name=name.value;
constexpr Base() { static_assert(someFunction(base_name)); }
};
class Derived1: public Base<"def">
{
public:
constexpr Derived1(int i): Base{}, i_{i} { }
void f() { i_+=1; std::cout << base_name << ' ' << i_ << '\n'; }
private:
int i_;
};
class Derived2: public Base<"ghi">
{
public:
constexpr Derived2(int i): Base{}, i_{i} { }
void f() { i_+=2; std::cout << base_name << ' ' << i_ << '\n'; }
private:
int i_;
};
/*
class BadDerived3: public Base<"jhi"> // static_assert() fails
{
public:
constexpr BadDerived3(int i): Base{}, i_{i} { }
void f() { i_+=3; std::cout << base_name << ' ' << i_ << '\n'; }
private:
int i_;
};
*/
int
main()
{
AAA<char> aaa{'A'};
BBB bbb{123};
CCC ccc{45.67};
std::cout << aaa.member << ' '
<< bbb.member << ' '
<< ccc.member << '\n'; // displays A 123 45.67
DDD<10> ddd{};
EEE eee{};
FFF fff{};
std::cout << ddd.value << ' '
<< eee.value << ' '
<< fff.value << '\n'; // displays 10 100 1000
// Base<"zbc"> bad_b; // static_assert() fails
Base<"abc"> b;
Derived1 d1{10};
Derived2 d2{20};
d1.f(); // displays def 11
d2.f(); // displays ghi 22
return 0;
}
EDIT
After a comment about C++20 support, here is a modified version that works with C++11.
We cannot provide our own type as template parameter, but references are allowed.
The trick is to create a constexpr
string-like data with static storage and reference it as a template parameter.
This is not as comfortable as the C++20 version, but it is still usable since each derived class only needs its constexpr
name just before.
All of this is shamelessly inspired from the various answers on this page.
/**
g++ -std=c++11 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <iostream>
struct constexpr_str
{
const char* const data;
const std::size_t size;
template<std::size_t N>
constexpr constexpr_str(const char(&d)[N]) : data{d}, size{N-1} { }
};
constexpr bool someFunction(constexpr_str n) { return n.data[0]<n.data[1]; }
template<const constexpr_str &name>
class Base
{
public:
static constexpr constexpr_str base_name{name};
constexpr Base() { static_assert(someFunction(base_name), "bad_name"); }
};
constexpr auto derived1_name=constexpr_str{"def"};
class Derived1: public Base<derived1_name>
{
public:
constexpr Derived1(int i): Base{}, i_{i} { }
void f() { i_+=1; std::cout << base_name.data << ' ' << i_ << '\n'; }
private:
int i_;
};
constexpr auto derived2_name=constexpr_str{"ghi"};
class Derived2: public Base<derived2_name>
{
public:
constexpr Derived2(int i): Base{}, i_{i} { }
void f() { i_+=2; std::cout << base_name.data << ' ' << i_ << '\n'; }
private:
int i_;
};
/*
constexpr auto derived3_name=constexpr_str{"jhi"};
class BadDerived3: public Base<derived3_name> // static_assert() fails
{
public:
constexpr BadDerived3(int i): Base{}, i_{i} { }
void f() { i_+=3; std::cout << base_name.data << ' ' << i_ << '\n'; }
private:
int i_;
};
*/
constexpr auto bad_base_name=constexpr_str{"zbc"};
constexpr auto base_name=constexpr_str{"abc"};
int
main()
{
constexpr auto txt=constexpr_str{"ABCD"};
static_assert(txt.size==4, "incorrect size");
static_assert(txt.data[1]=='B', "incorrect char");
// Base<bad_base_name> bad_b; // static_assert() fails
Base<base_name> b;
Derived1 d1{10};
Derived2 d2{20};
d1.f(); // displays def 11
d2.f(); // displays ghi 22
return 0;
}