Я хотел бы определить во время компиляции, если указатель на Полученный может быть брошен от указателя для Базирования без dynamic_cast <>. Является это возможное использование шаблонами и метапрограммированием? Это не точно та же проблема как определение, если Основой является виртуальный базовый класс Полученных, потому что Основой мог быть суперкласс виртуального базового класса Полученных.
Спасибо, Tim Update: Я чувствовал себя хорошо об этом методе:
#include <iostream>
using namespace std;
class Foo
{
};
class Bar : public Foo
{
};
class Baz : public virtual Foo
{
};
class Autre : public virtual Bar
{
};
typedef char Small;
class Big { char dummy[2]; };
template<typename B, typename D>
struct is_static_castable
{
const B* foo;
char bar[1];
static Small test(char(*)[sizeof(static_cast<const D*>(foo)) == sizeof(const D*)]);
static Big test(...);
enum { value = (sizeof(test(&bar)) == sizeof(Small)) };
};
int main()
{
cout << "Foo -> Bar: " << is_static_castable<Foo, Bar>::value << "\n";
cout << "Foo -> Baz: " << is_static_castable<Foo, Baz>::value << "\n";
cout << "Foo -> Autre: " << is_static_castable<Foo, Autre>::value << "\n";
}
Но это не работает с gcc:
multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Baz>’:
multi-fun.cpp:38: instantiated from here
multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Baz’ via virtual base ‘Foo’
multi-fun.cpp:29: error: array bound is not an integer constant
multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Autre>’:
multi-fun.cpp:39: instantiated from here
multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Autre’ via virtual base ‘Bar’
multi-fun.cpp:29: error: array bound is not an integer constant
Я смущен тем, что может быть сделано с sizeof () прием?
У меня была такая же проблема, однажды. К сожалению, я не совсем уверен в виртуальности проблемы. Но: В Boost есть класс is_base_of
(см. здесь), который позволит вам сделать что-то вроде следующего
BOOST_STATIC_ASSERT((boost::is_base_of<Foo, Bar>::value));
Кроме того, есть класс is_virtual_base_of
в type_traits
Boost, возможно, это то, что вы ищете.
Возможно, это немного наивно (я гораздо сильнее в C, чем в C++), поэтому я могу не понять, что вы пытаетесь сделать, но если вы говорите о приведении указателей, то приведение в стиле C отлично работает (например, (D *)foo
), или эквивалентный C++ reinterpret_cast. Тем не менее, это может быть очень опасно, потому что у вас нет никакой проверки во время выполнения, и поэтому вы должны быть уверены, что приводите к правильному типу. И опять же, если вы хотите иметь простой способ проверить, правильно ли это предположение или нет, мы вернемся к началу. Однако, похоже, что вы пытаетесь сравнить указатели, которые все одинаковы (в основном это целые числа). Насколько я знаю, в C++ нет способа определить класс объекта во время выполнения, включая sizeof, который работает во время компиляции. В принципе, нет способа сделать то, что вы хотите (по крайней мере, в стандартном C++), однако само приведение не вызовет никаких проблем, только использование нового приведенного указателя не по назначению. Если вам абсолютно необходима эта функциональность, то лучше всего будет включить виртуальную функцию в ваш базовый класс, которая сообщает, какой это класс (предпочтительно с перечислительным значением), и перегрузить ее в том подклассе, который вы надеетесь определить, можно ли делать приведение.
После преобразования в базовый указатель можно получить только ошибку времени выполнения (dynamic_cast). Вы можете определить методы, используя шаблонные параметры, и получить ошибку компиляции, используя специализации шаблонов.
Вот решение для перенаправления компилятора на то, чтобы сделать что-то в зависимости от того, является ли класс подклассом другого класса или нет.
class A
{};
class B : virtual public A
{};
class C : public A
{};
// Default template which will resolve for
// all classes
template
< typename T
, typename Enable = void
>
struct FooTraits
{
static void foo(){
std::cout << "normal" << std::endl;
}
};
// Specialized template which will resolve
// for all sub classes of A
template
< typename T
>
struct FooTraits
< T
, typename boost::enable_if
< boost::is_virtual_base_of< A, T>
>::type
>
{
static void foo(){
std::cout << "virtual base of A" << std::endl;
}
};
int main(int argc, const char * argv[] ){
FooTraits<C>::foo(); // prints "normal"
FooTraits<B>::foo(); // prints "virtual base of A"
}
и если вы хотите узнать, как Boost это сделал. Если у вас есть класс Base и класс Derived, то имеет место следующее.
struct X : Derived, virtual Base
{
X();
X(const X&);
X& operator=(const X&);
~X()throw();
};
struct Y : Derived
{
Y();
Y(const Y&);
Y& operator=(const Y&);
~Y()throw();
};
bool is_virtual_base_of = (sizeof(X)==sizeof(Y)));
Это трюк использования виртуального наследования с множественным наследованием. Множественное наследование от одной и той же виртуальной базы не приводит к появлению дубликатов виртуального базового класса, и поэтому вы можете проверить это с помощью sizeof.
Если вы хотите знать во время компиляции, вы можете взять производный класс в качестве параметра но если единственное, что у вас есть, это Base, тогда вы не сможете узнать, относится ли он к какому-либо классу foo, bar и т. д. Эта проверка может быть выполнена только в том случае, если указатель преобразован в базу. Я думаю, что в этом вся цель dynamic_cast <>
Во-первых, ваш код выполняет sizeof указателя вместо разыменованного указателя, поэтому он не будет работать, даже если gcc не будет жаловаться.
Во-вторых, трюк sizeof должен работать с приведением 0, а не с фактическими указателями или объектами — это гарантирует нулевые накладные расходы, а также то, что он не будет выполняться, пока вы не сделаете это правильно.
В-третьих, вам нужно объявить 2 шаблонных класса или структуры, одна производная только от D, другая производная от D и виртуального B, а затем привести 0 к их указателям, разыменовать их, а затем sizeof.
4th — Есть ли у вас веская причина пытаться быть политкорректным с помощью static_cast вместо прямого приведения? Компилятор всегда сделает из этого вывод, что вы ищете больше нытья, а в данном случае точно нет.
Кстати, вам не нужно брать полный код у Александреску — просто возьмите основную технику, которая в основном просто:
sizeof(*((T*)0))
Александреску действительно хорош в уборке после трюка.
О, и помните, что компилятор не должен оценивать sizeof args или создавать экземпляры неиспользуемых шаблонных классов и структур — так что если это происходит, то это ошибка компилятора, и если вы заставляете его делать это, то это ваша ошибка :-)
После того, как вы это сделаете, вам нужно точно и в позитивных терминах определить, что ваше утверждение «если указатель на Derived может быть преобразован из указателя на Base без dynamic_cast<>» на самом деле означает с точки зрения отношений классов — просто говоря «без оператора/ функция Q" не делает проблему четко определенной, и вы не можете решить то, что не можете определить - честно :-)
Так что просто сделайте первый чистый шаг, который компилируется, а затем попытайтесь определить, каким образом два случаи, о которых вы упомянули, в действительности различны - что бы один имел или сделал бы, чего не сделал бы другой.