简体   繁体   中英

Check template parameter of Base class in Derived

I have a base class like

template <int parameter>
class Base {
public:
  virtual int getMaxParameter() = 0;      
}

and I chose one of multiple derived classes like

template <int parameter>
class DerivedA : public Base<parameter> 
{
public:
  int getMaxParameter() { return 2; }
}

via

const int p = 3;
Base<p>* base;
switch (unreleated_input_int) {
  case 1:
    base = new DerivedA<p>(); break;
  case 2:
    base = new DerivedB<p>(); break;
  // ...
}

Q: How can I make sure, that the template parameter of Base & Derived does not exceed the return value of getMaxParameter()?

I could do something like

DerivedA() {
  if (parameter > getMaxParameter()) 
    //...
}

but I don't want to write this in every derived class. Can I do something like this in Base? I can't call pure virtual functions, but is there another way?

I dont need the virtual function, it could be eg a member variable of Derived instead.

You could use static_assert (a compile-time assertion) to make DerivedA<3> (where the condition 3 < 2 is not met) fail compilation (if you like that):

template <int parameter>
class DerivedA : public Base<parameter> 
{
    static_assert(parameter < 2, "Parameter for DerivedA is too large!");
    //            ^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //              condition           message in case of error
public:
};

This will give you a nice hint if you write DerivedA<3> :

error: static asertion failed: Parameter for DerivedA is too large!

You could turn 2 into a constexpr member. constexpr is an indication that it's known during compile-time, which is required for static_assert :

template <int parameter>
class DerivedA : public Base<parameter> 
{
    static constexpr int maxParameter = 2;
    static_assert(parameter < maxParameter, "Parameter for DerivedA is too large!");
public:
};

If you dislike to have this code in each derived class, you can move it to Base , which now takes two parameters: the actual parameter and the maximum value. It has the disadvantage that the error message can't include the derived class name, but I guess this is not very important:

template <int parameter, int maxParameter>
class Base
{
    static_assert(parameter < maxParameter, "Parameter is too large!");
};

template <int parameter>
class DerivedA : public Base<parameter, 2> 
{
};

If you don't like the second template parameter for the Base class (like it is the problem in your added factory code in your question), you can move it to the Base constructor. It's a bit tricky to invoke the templated constructor, but possible with the help of template type deduction and a dummy parameter :

template <int parameter>
class Base
{
public:
    template <int maxParameter>
    Base(std::integral_constant<int, maxParameter>) {
        static_assert(parameter < maxParameter, "Parameter is too large!");
    }
};

template <int parameter>
class DerivedA : public Base<parameter> 
{
public:
    DerivedA() : Base(std::integral_constant<int, 2>()) {
        // ...
    }
};

If you can't use a static_assert at compile time because the return value of getMaxParameter of your derived class is not known yet, you can use a factory...

#include <iostream>

template <int parameter>
struct Base {
  void verify () {
    if (parameter > this->getMaxParameter())
      std::cout << "Bigger\n";
    else
      std::cout << "Smaller\n";
  }
  virtual int getMaxParameter() = 0;
};

template <int parameter>
struct Derived : public Base<parameter> {
  virtual int getMaxParameter() { return 0; }
};

template <int parameter>
Base<parameter> * make_and_verify () {
  Base<parameter> * result = new Derived<parameter>;
  result->verify();
  return result;
}

int main() {
  Base<0> * foo = make_and_verify<0>();
  Base<1> * bar = make_and_verify<1>();
  return 0;
}

verified output

The class is created at compile time as a template instantiation.

The best you can do is a static_assert is the value to be checked against is a constexpr :

template <int parameter>
class DerivedA : public Base<parameter> 
{
public:
  static_assert(parameter < 2, "wrong template parameter value");

  int getMaxParameter() { return 2; }
};

Or, assuming you can make getMaxParameter a constexpr :

template <int parameter>
class DerivedA : public Base<parameter> 
{
public:
  constexpr int getMaxParameter() { return 2; }
  static_assert(parameter < getMaxParameter(), "wrong template parameter");
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM