0

Is it possible to have a template base class and a non-template derived class? If so, what if the base class is a non-type template?

I am trying to pass a constexpr string to a non-template derived class but want to check the string during compile time in the class. I can make it work if I make the class a template but is there a way to do so if not a template?

An example of what I hope could work:

template<const char* name>
class Base
{
public:
    constexpr Base(){
        someFunction(name);
    }

    const char* base_name = name;
};

class Derived: public Base
{
public:
    constexpr Derived(const char* name)
    {
        Base<name>();
    }

}
domoremath
  • 555
  • 2
  • 8
  • 17

1 Answers1

0

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;
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30