Я просто хочу убедиться, что T на самом деле является экземпляром подкласса или самого класса. Код внутри функции, которую я предоставил, практически не имеет значения.
phant0m 04
6
напротив, это очень актуально. Он определяет, стоит ли выполнять этот тест - хорошая идея или нет. Во многих (всех?) Случаях нет абсолютно никакой необходимости устанавливать такие ограничения самостоятельно, а лучше позволить компилятору сделать это при создании экземпляра. Например, для принятого ответа было бы хорошо поставить проверку, Tполучено ли из Baseclass. На данный момент эта проверка является неявной и не видна для разрешения перегрузки. Но если нигде такое неявное ограничение не применяется, похоже, нет причин для искусственного ограничения.
Йоханнес Шауб - лит
1
Да, я согласен. Однако я просто хотел знать, есть ли способ добиться этого или нет :) Но, конечно, у вас есть очень веский аргумент, и спасибо за понимание.
phant0m 04
Ответы:
53
В этом случае вы можете:
template <classT>voidfunction(){
Baseclass *object = new T();
}
Это не будет компилироваться, если T не является подклассом Baseclass (или T является базовым классом).
ах да, это хорошая идея. благодаря! Я так понимаю, что нет способа определить это в определении шаблона?
phant0m 04
2
@ phant0m: Верно. Вы не можете явно ограничить параметры шаблона (за исключением использования концепций, которые были рассмотрены для c ++ 0x, но затем отброшены). Все ограничения неявно возникают в результате операций, которые вы выполняете над ним (или, другими словами, единственным ограничением является «Тип должен поддерживать все операции, которые выполняются над ним»).
sepp2k 04
1
ай ic. Большое спасибо за разъяснения!
phant0m 04
8
Это выполняет конструктор T () и требует наличия конструктора T (). См. Мой ответ, как избежать этих требований.
Дуглас Лидер 05
3
Красиво и понятно, но это проблема, если Т - «тяжелый» класс.
3ave
84
С компилятором, совместимым с C ++ 11, вы можете сделать что-то вроде этого:
template<classDerived>classMyClass {
MyClass() {
// Compile-time sanity checkstatic_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");
// Do other construction related stuff...
...
}
}
Я тестировал это с помощью компилятора gcc 4.8.1 в среде CYGWIN, поэтому он должен работать и в средах * nix.
Для меня это работает так же: template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
Маттиас Дитер Валлнефер
1
Я думаю, что это наиболее читаемый ответ, позволяющий избежать лишнего кода во время выполнения.
Kyle
50
Чтобы выполнить менее бесполезный код во время выполнения, вы можете посмотреть:
http://www.stroustrup.com/bs_faq2.html#constraints,
который предоставляет некоторые классы, которые эффективно выполняют тест времени компиляции и создают более приятные сообщения об ошибках.
Для меня это лучший и самый интересный ответ. Обязательно ознакомьтесь с FAQ Страуструпа, чтобы узнать больше обо всех видах ограничений, которые вы можете применить аналогично этому.
Это отличный ответ. Есть ли хорошие способы избежать предупреждений unused variable 'p'и unused variable 'pb'?
Филип С.
@FilipS. добавить (void)pb;после B* pb = p;.
bit2shift
11
Вам не нужны концепции, но вы можете использовать SFINAE:
template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function(){
// This function will only be considered by the compiler if// T actualy derived from Base
}
Обратите внимание, что это создаст экземпляр функции только при выполнении условия, но не приведет к существенной ошибке, если условие не будет выполнено.
Что, если вы обернете все функции таким образом? кстати, что он возвращает?
the_drow
enable_ifЗанимает второй тип параметра , который по умолчанию void. Выражение enable_if< true, int >::typeпредставляет тип int. Я не могу понять, в чем заключается ваш первый вопрос, вы можете использовать SFINAE для чего угодно, но я не совсем понимаю, что вы собираетесь делать с этим для всех функций.
Дэвид Родригес - dribeas 04
7
Начиная с C ++ 11, вам не нужны Boost или static_assert. C ++ 11 представляет is_base_of и enable_if. C ++ 14 вводит удобный тип enable_if_t, но если вы застряли на C ++ 11, вы можете просто использовать enable_if::typeвместо него.
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Альтернатива 2
Начиная с C ++ 17, у нас есть is_base_of_v. В дальнейшем решение можно переписать так:
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Альтернатива 3
Вы также можете просто ограничить весь шаблон. Вы можете использовать этот метод для определения целых классов. Обратите внимание, как enable_if_tбыл удален второй параметр (ранее он был установлен на void). Его значение по умолчанию - на самом деле void, но это не имеет значения, поскольку мы его не используем.
#include<type_traits>usingnamespacestd;
template <typename T,
typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Из документации параметров шаблона мы видим, что typename = enable_if_t...это параметр шаблона с пустым именем. Мы просто используем его, чтобы гарантировать, что определение типа существует. В частности, enable_if_tне будет определен, если Baseне является базой T.
Приведенная выше методика приведена в качестве примера в enable_if.
T
получено ли изBaseclass
. На данный момент эта проверка является неявной и не видна для разрешения перегрузки. Но если нигде такое неявное ограничение не применяется, похоже, нет причин для искусственного ограничения.Ответы:
В этом случае вы можете:
template <class T> void function(){ Baseclass *object = new T(); }
Это не будет компилироваться, если T не является подклассом Baseclass (или T является базовым классом).
источник
С компилятором, совместимым с C ++ 11, вы можете сделать что-то вроде этого:
template<class Derived> class MyClass { MyClass() { // Compile-time sanity check static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass"); // Do other construction related stuff... ... } }
Я тестировал это с помощью компилятора gcc 4.8.1 в среде CYGWIN, поэтому он должен работать и в средах * nix.
источник
template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");
...Чтобы выполнить менее бесполезный код во время выполнения, вы можете посмотреть: http://www.stroustrup.com/bs_faq2.html#constraints, который предоставляет некоторые классы, которые эффективно выполняют тест времени компиляции и создают более приятные сообщения об ошибках.
В частности:
template<class T, class B> struct Derived_from { static void constraints(T* p) { B* pb = p; } Derived_from() { void(*p)(T*) = constraints; } }; template<class T> void function() { Derived_from<T,Baseclass>(); }
источник
unused variable 'p'
иunused variable 'pb'
?(void)pb;
послеB* pb = p;
.Вам не нужны концепции, но вы можете использовать SFINAE:
template <typename T> boost::enable_if< boost::is_base_of<Base,T>::value >::type function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Обратите внимание, что это создаст экземпляр функции только при выполнении условия, но не приведет к существенной ошибке, если условие не будет выполнено.
источник
enable_if
Занимает второй тип параметра , который по умолчаниюvoid
. Выражениеenable_if< true, int >::type
представляет типint
. Я не могу понять, в чем заключается ваш первый вопрос, вы можете использовать SFINAE для чего угодно, но я не совсем понимаю, что вы собираетесь делать с этим для всех функций.Начиная с C ++ 11, вам не нужны Boost или
static_assert
. C ++ 11 представляетis_base_of
иenable_if
. C ++ 14 вводит удобный типenable_if_t
, но если вы застряли на C ++ 11, вы можете просто использоватьenable_if::type
вместо него.Альтернатива 1
Решение Давида Родригеса можно переписать следующим образом:
#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of<Base, T>::value, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Альтернатива 2
Начиная с C ++ 17, у нас есть
is_base_of_v
. В дальнейшем решение можно переписать так:#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of_v<Base, T>, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Альтернатива 3
Вы также можете просто ограничить весь шаблон. Вы можете использовать этот метод для определения целых классов. Обратите внимание, как
enable_if_t
был удален второй параметр (ранее он был установлен на void). Его значение по умолчанию - на самом делеvoid
, но это не имеет значения, поскольку мы его не используем.#include <type_traits> using namespace std; template <typename T, typename = enable_if_t<is_base_of_v<Base, T>>> void function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Из документации параметров шаблона мы видим, что
typename = enable_if_t...
это параметр шаблона с пустым именем. Мы просто используем его, чтобы гарантировать, что определение типа существует. В частности,enable_if_t
не будет определен, еслиBase
не является базойT
.Приведенная выше методика приведена в качестве примера в
enable_if
.источник
template <class T : Base>
Вы можете использовать подталкивание Concept Check «ы
BOOST_CONCEPT_REQUIRES
:#include <boost/concept_check.hpp> #include <boost/concept/requires.hpp> template <class T> BOOST_CONCEPT_REQUIRES( ((boost::Convertible<T, BaseClass>)), (void)) function() { //... }
источник
Вызывая внутри вашего шаблона функции, существующие в базовом классе.
Если вы попытаетесь создать экземпляр своего шаблона с типом, который не имеет доступа к этой функции, вы получите ошибку времени компиляции.
источник
T
это a,BaseClass
потому что объявленные членыBaseClass
могут повторяться в объявленииT
.