WilliamKF
WilliamKF

Reputation: 43209

How can I create a compile time assertion that a template is of specific types?

I have a template function and wish to ensure at compile time that it is not instantiated on a subtype or supertype of a particular class.

How can I cause a C++ compiler error if this is violated?

class base {
};
class derived : public base {
};
class lowest : public derived {
};

template <typename T>
bool isCorrect(const T& obj) {
  typedef foo<T> D;
  foo<T> *def = foo<T>::find();
  return (def && def->getAnswer(object));
}

I want isCorrect to only be available for class derived, but not base or lowest. Note there could be many other lowest classes and a string of base classes to be excluded as well as alternative derived classes that are acceptable.

Is there a way in C++ to limit the template to only apply to the derived classes I explicitly specify?

Upvotes: 5

Views: 1518

Answers (3)

Jon
Jon

Reputation: 437854

Here's one technique that I know of.

First, make another template class policy_enforcer. Declare this class without defining it, and also provide a specialization of it for derived that is also defined:

template<typename T> struct policy_enforcer;
template<> struct policy_enforcer<derived> { };

Then, within the function you wish to lock down, include the expression sizeof(policy_enforcer<T>). Since sizeof on incomplete types is a compilation error, this will prevent the code from compiling.

Updated with live code: using base, using derived, using lowest.

Upvotes: 4

stefanB
stefanB

Reputation: 79930

You can use template specialization.

You can implement only the isCorrect only for types that you want to be able to work with.

For the other types you can implement dummy method, returning false for example, or not implement isCorrect at all, in which case it will not compile for other types.

#include <iostream>

using namespace std;

class base {
};
class derived : public base {
};
class lowest : public derived {
};

// using this it will fail if you try to pass anything
// else than `derived`
//  template <typename T>
//     bool isCorrect(const T& obj);

template <typename T>
bool isCorrect(const T& obj) {
    cout << __PRETTY_FUNCTION__ << endl;
    return false;
}

template <>
bool isCorrect<derived>(const derived& obj) {
    cout << __PRETTY_FUNCTION__ << endl;
    return true;
//  typedef foo<derived> D;
//  foo<derived> *def = foo<derived>::find();
//  return (def && def->getAnswer(object));
}

Test:

int main()
{
    base b;
    derived d;
    lowest l;

    cout << isCorrect(b) << endl;
    cout << isCorrect(d) << endl;
    cout << isCorrect(l) << endl;
}

Output:

bool isCorrect(const T&) [with T = base]
0
bool isCorrect(const T&) [with T = derived]
1
bool isCorrect(const T&) [with T = lowest]
0

Upvotes: 1

Xeo
Xeo

Reputation: 131907

Type traits, specifically is_base_of.

#include <type_traits>

template <typename T>
bool isCorrect(const T& obj) {
  static bool const is_base = std::is_base_of<base, T>::value;
  static bool const derives = std::is_base_of<derived, T>::value;
  // specify allowed types here
  static bool const is_derived = std::is_same<T, derived>::value;
  // ---
  static_assert((!is_base && !derives) || is_derived, "wrong argument type");

  typedef foo<T> D;
  foo<T> *def = foo<T>::find();
  return (def && def->getAnswer(object));
}

Note that this is C++11 specific, but you can get the same behaviour with Boost.TypeTraits.

Upvotes: 10

Related Questions