Помогите с типографическими признаками.
-
20-09-2019 - |
Вопрос
Предположим, у нас есть следующий класс шаблона
template<typename T> class Wrap { /* ... */ };
Мы невозможно изменить Wrap
.Это важно.
Пусть существуют классы, производные от Wrap<T>
.Например,
class NewInt : public Wrap<int> { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
class Foo : public Wrap<Bar> { /* ... */ };
Мы невозможно изменить эти классы тоже.Все классы выше являются сторонними.Они не мои.
Мне нужно следующее время компиляции type_traits
:
template<class T>
struct is_derived_from_Wrap {
static const bool value = /* */;
};
Что мне нужно?
assert(is_derived_from_Wrap<Int>::value == true); // Indeed I need static assert
assert(is_derived_from_Wrap<MyClass>::value == true);
assert(is_derived_from_Wrap<char>::value == false);
struct X {};
assert(is_derived_from_Wrap<X>::value == false);
Решение
Вы можете сделать это с помощью SFINAE, но это своего рода волшебство, если вы не знаете, что происходит...
template<typename T> class Wrap { };
struct myclass {};
struct X {};
class Int : public Wrap<int> { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
template< typename X >
struct is_derived_from_Wrap
{
struct true_type { char _[1]; };
struct false_type { char _[2]; };
template< typename U >
static true_type test_sfinae( Wrap<U> * w);
static false_type test_sfinae( ... );
enum { value = sizeof( test_sfinae( (X*)(0) ) )==sizeof(true_type) };
};
#include <iostream>
#define test(X,Y) std::cout<<( #X " == " #Y )<<" : "<<( (X)?"true":"false") <<std::endl;
int main()
{
test(is_derived_from_Wrap <Int>::value, true);
test(is_derived_from_Wrap <MyClass>::value, true);
test(is_derived_from_Wrap <char>::value, false);
test(is_derived_from_Wrap <X>::value, false);
}
Это дает ожидаемый результат
is_derived_from_Wrap <Int>::value == true : true
is_derived_from_Wrap <MyClass>::value == true : true
is_derived_from_Wrap <char>::value == false : false
is_derived_from_Wrap <X>::value == false : false
С моим кодом есть пара ошибок.Он также вернет true, если тип является Wrap.
assert( is_derived_from_Wrap< Wrap<char> >::value == 1 );
Вероятно, это можно исправить, если потребуется, используя немного больше магии SFINAE.
Он вернет false, если вывод не является общедоступным (т.е. является частным или защищенным).
struct Evil : private Wrap<T> { };
assert( is_derived_from_Wrap<Evil>::value == 0 );
Я подозреваю, что это невозможно исправить.(Но я могу ошибаться).Но я подозреваю, что публичного наследования достаточно.
Другие советы
Вам нужно выполнить довольно сложное метапрограммирование шаблонов, чтобы определить, является ли один класс X производным от другого Y в общем случае.В основном X происходит от Y, если:
- Y можно неявно преобразовать в X
- X и Y не одного типа
Андрей Александреску объясняет, как это сделать (наряду со многими другими трюками с шаблонами) в своей книге «Современный дизайн на C++».
Вы можете найти код, который решает вашу проблему, либо в Локи библиотека, или УСТЛ реализация, оба написаны Александреску.
Следующее определяет, является ли что-то является обертка:
template<class T>
struct is_Wrap { static const bool value = false; };
template<typename T>
struct is_Wrap< Wrap<T> > { static const bool value = true; };
Поскольку деривация представляет собой отношение Есть-А, все, полученное из Wrap<T>
также является Wrap<T>
и должен быть найден этим.