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

20

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

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

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

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

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

    
задан san 11.08.2011 в 04:50
источник
  • Под «подтипом» вы подразумеваете производный класс? –  ildjarn 11.08.2011 в 04:53
  • @ildjarn Да, вот что я имею в виду. –  san 11.08.2011 в 05:00
  • См. также stackoverflow.com/questions/2631585/... –  eh9 25.02.2014 в 19:25

3 ответа

40

Учитывая некоторый полный тип 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
};

Если вы используете компилятор C ++ 03 с TR1, вы можете использовать std::tr1::is_base_of вместо boost::is_base_of ; если вы используете компилятор C ++ 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

    
ответ дан ildjarn 11.08.2011 в 05:09
источник
2

Из «Современного дизайна C ++», глава 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.

    
ответ дан Alex B 11.08.2011 в 05:01
источник
  • И как вы вызываете ошибку во время компиляции, если она не равна? Хороший аватар btw, любил эту игру. –  Seth Carnegie 11.08.2011 в 05:05
  • @ Сет, спасибо. :) Обновлено –  Alex B 11.08.2011 в 05:10
  • Проблема с этим подходом заключается в том, что он не проходит для защищенных / частных наследований. Вы можете увидеть этот вопрос для is_base_of, чтобы узнать, как именно он реализован для всех типов наследований. –  iammilind 11.08.2011 в 07:34
0

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

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

    
ответ дан Seth Carnegie 11.08.2011 в 04:53
источник
  • @san, что ломает много приложений и гибкость шаблонов. Почему бы просто не принять Base * и использовать полиморфизм вместо использования шаблонов? Это похоже на то, что вам нужно. –  Seth Carnegie 11.08.2011 в 05:06
  • Я не понимаю, почему это может сломать другие приложения (если это очевидно, несите меня, все это ново для меня) Я не хочу, чтобы все компиляторы имели эту обязательную проверку. Просто хотите включить его (необязательно) в одном классе. База *, которую я имею в виду, это просто класс с несколькими статическими функциями, я не хочу выделять память для реального объекта. –  san 11.08.2011 в 05:12
  • @san Я имею в виду, что это нарушит другие приложения ваших функций, то есть сколько мест и способов их применения. Кроме того, вам не нужно выделять другой объект для указания указателя подкласса на указатель родительского класса и использования полиморфизма. Вы действительно не должны использовать шаблоны для этого. –  Seth Carnegie 11.08.2011 в 05:14
  • @san, если вы покажете мне свой код шаблона, я покажу вам, как преобразовать его в код без шаблонов, используя полиморфизм. –  Seth Carnegie 11.08.2011 в 05:15
  • @san oh, вы просто используете классы для группировки статических функций? Вы не можете получить доступ к ним через указатели, поэтому для этого вам нужны шаблоны, но тогда это не поможет вам вообще проверить, наследуется ли класс от другого класса, поскольку статические функции не наследуются. Возможно, вам нужно передавать объекты функций вместо использования шаблонов для вызова статических функций класса. –  Seth Carnegie 11.08.2011 в 06:50