Как убедиться, что параметр шаблона является подтипом требуемого типа?

У меня есть класс шаблона, я хочу сделать следующее

  • Убедитесь, что объект создается только в том случае, если переданный параметр шаблона является подтипом желаемого типа
  • Общайтесь с пользователем кода перед тем, что параметр шаблона должен удовлетворять

(1) автоматически рассматривается в том смысле, что если переданный параметр шаблона не поддерживает некоторую функцию, которую класс использует, код не будет компилироваться. Но эта ошибка может быть обнаружена довольно поздно. Я хочу, чтобы чеки были как можно раньше. То, что я также хочу сделать, заключается в том, что должно быть очевидно, что передаваемый параметр шаблона должен быть получен из базового типа, который я предоставляю.

Во-первых, это ошибочно? и если нет, то как мне это сделать? (простейший способ, пожалуйста, С++ все еще новичок для меня)

Благодаря stackoverflow, вы действительно ускорили мою скорость обучения С++.

Ответ 1

Учитывая некоторый полный тип MyBase, следующее приведет к ошибке времени компиляции, если T не получен из MyBase:

#include <boost/type_traits/is_base_of.hpp>
#include <boost/static_assert.hpp>

template<typename T>
class Foo {
    BOOST_STATIC_ASSERT_MSG(
        (boost::is_base_of<MyBase, T>::value),
        "T must be a descendant of MyBase"
    );
    // Foo implementation as normal
};

Если вы используете компилятор С++ 03 с TR1, вы можете использовать std::tr1::is_base_of вместо boost::is_base_of; если вы используете компилятор С++ 11, вы можете использовать std::is_base_of вместо boost::is_base_of и ключевое слово static_assert вместо макроса BOOST_STATIC_ASSERT_MSG:

#include <type_traits>

template<typename T>
class Foo {
    static_assert(
        std::is_base_of<MyBase, T>::value, 
        "T must be a descendant of MyBase"
    );
    // Foo implementation as normal
};

N.b. это даст true_type для частных и неоднозначных производных типов, поэтому этого недостаточно, если вам действительно нужно лечить T as-a MyBase (в большинстве контекстов).

Ссылки Doc:
Boost. StaticAssert
Boost. TypeTraits

Ответ 2

Из "Modern С++ Design", глава 2.7 ( "Обнаружение конвертируемости и наследования во время компиляции" ): вы можете использовать трюк sizeof:

typedef char Small;
class Big { char dummy[2]; };

Small Test(Base1);
Big Test(...);

const bool isSubclassOfBase1 = sizeof(Test(Derived1())) == sizeof(Small);

Он использует тот факт, что sizeof(...) может определить тип, который выражение оценивает, и поскольку значения возвращаемого значения имеют разные размеры, проверка == оценивается как true или false. Если Derived1 действительно является базой Base1, выбрана перегрузка Small Test(Base1); и будет isSubclassOfBase1.

Расширяясь от этого, вы можете инкапсулировать проверку и сделать статическое утверждение, чтобы он не выполнялся во время компиляции:

#include <boost/static_assert.hpp>

class A {};
class B: public A {};
class C {};

template<class Base, class Derived>
struct SubclassCheck
{
    typedef char Yes;
    class No { char dummy[2]; };

    static Yes Test(Base);
    static No Test(...);
    enum {
        Value = (sizeof(Test(*(Derived*)NULL)) == sizeof(Yes))
    };
};

#define CHECK_DERIVES(b,d)\
    BOOST_STATIC_ASSERT((SubclassCheck<b,d>::Value));

int
main()
{
    CHECK_DERIVES(A, B);
    // CHECK_DERIVES(A, C); // fails
}

Вы можете использовать любую другую статическую реализацию утверждения, не обязательно Boost.

Ответ 3

Да, это автоматически берется в том случае, если параметр не поддерживает то, что вы делаете с ним, это приведет к ошибке компиляции. Проверка вручную, если тип является подтипом другого типа, может выполняться только (AFAIK) во время выполнения, что намного позже времени компиляции. Я не знаю, что вы подразумеваете под этой ошибкой, которую обнаруживают поздно, время компиляции уже достигнуто. Кроме того, если все проверяли тип параметров своего шаблона, STL не мог работать с указателями, а также с фактическими итераторами на основе классов и не был бы почти таким же гибким.

Что касается того, чтобы ваш пользователь знал требования, просто укажите их в документации.