Отправка на основе типа компиляции
Следуя методам "Modern С++ Design", я реализую библиотеку персистентности с различными оптимизациями во время компиляции. Я хотел бы, чтобы возможность отправлять функцию в шаблонную переменную-член, если эта переменная получена из данного класса:
template<class T, template <class> class Manager = DefaultManager> class Data
{
private:
T *data_;
public:
void Dispatch()
{
if(SUPERSUBCLASS(Container, T))
{
data_->IKnowThisIsHere();
}
else
{
Manager<T>::SomeGenericFunction(data_);
}
}
}
Где SUPERSUBCLASS - это макрос времени компиляции для определения наследования объектов. Конечно, это не удается во всех случаях, когда T наследует от Container (или T является внутренним типом и т.д.), Потому что компилятор справедливо жалуется, что IKnowThisIsHere() не является членом данных, хотя этот путь кода никогда не будет соблюден, как показано здесь после предварительной обработки с помощью T = int:
private:
int *data_;
public:
void Dispatch()
{
if(false)
{
data_->IKnowThisIsHere();
Компилятор явно жалуется на этот код, хотя он никогда не будет выполнен. Предложение об использовании dynamic_cast также не работает, поскольку повторное преобразование типа выполняется во время компиляции, что невозможно (например, с T = double, std::string):
void Dispatch()
{
if(false)
{
dynamic_cast<Container*>(data_)->IKnowThisIsHere();
error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)
Мне действительно нужно подражать (или действительно убеждать!), когда компилятор испускает один набор кода, если T наследует от Container, а другой, если он этого не делает.
Любые предложения?
Ответы
Ответ 1
Перегрузка может быть полезна для реализации диспетчеризации времени компиляции, как предложено Александреску в его книге "Современный дизайн С++".
Вы можете использовать такой класс, чтобы преобразовать во время компиляции логическое или целое число в тип:
template <bool n>
struct int2type
{ enum { value = n}; };
Следующий исходный код показывает возможное приложение:
#include <iostream>
#define MACRO() true // <- macro used to dispatch
template <bool n>
struct int2type
{ enum { value = n }; };
void method(int2type<false>)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void method(int2type<true>)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
int
main(int argc, char *argv[])
{
// MACRO() determines which function to call
//
method( int2type<MACRO()>());
return 0;
}
Конечно, что действительно делает работу MACRO() или лучшей реализацией в качестве метафлора
Ответ 2
Вам требуется время компиляции if
. Затем это вызывает функцию, зависящую от того, какой случай true
. Таким образом, компилятор не наткнется на код, который он не может скомпилировать (потому что он безопасно хранится в другом шаблоне функции, который никогда не создается экземпляром).
Существует несколько способов реализации такого времени компиляции if
. Наиболее распространенным является использование идиомы SFINAE: сбой замены не является ошибкой. Boost is_base_of
ist фактически является экземпляром этой идиомы. Чтобы использовать его правильно, вы не будете писать его в выражении if
, а скорее используйте его как возвращаемый тип своей функции.
Неподтвержденный код:
void Dispatch()
{
myfunc(data_);
}
private:
// EDIT: disabled the default case where the specialisation matched
template <typename U>
typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) {
data_->IKnowThisIsHere();
}
template <typename U>
typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case
Manager<U>::SomeGenericFunction(data_);
}
Ответ 3
У более высоких признаков есть что-то для этого: is_base_of
Ответ 4
Посмотрите на библиотеку метапрограммного расширения boost. Кроме того, в зависимости от того, что вы пытаетесь выполнить, посмотрите на библиотеку сериализации boost, поскольку она может уже иметь то, что вам нужно.
Ответ 5
Я заинтересован в том, чтобы делать это "с первых принципов" как образовательное любопытство. Тем не менее, я посмотрю на библиотеки Boost.
В любом случае, я не думаю, что is_base_of - любая помощь - он делает точно так же, как макрос SUPERSUBCLASS...
Ответ 6
К сожалению, я тоже прошел через это (и это тоже вызов во время выполнения;)). Компилятор жалуется, что если вы передаете не полиморфные или типы классов, аналогично предыдущему:
error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
или
error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)