我想在编译时确定是否可以从没有dynamic_cast<>的指针转换为指向Derived的指针。这可能使用模板和元编程吗?这与确定Base是否是Derived的虚拟基类不完全相同,因为Base可能是Derived虚拟基类的超类。
谢谢, 蒂姆 更新: 我觉得这种方法很好:
#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()技巧可以做些什么感到困惑?
答案 0 :(得分:11)
我有同样的问题,一次。不幸的是,我不太确定虚拟问题。但是:Boost有一个名为is_base_of
的类(见here),它可以让你做一些事情。如下
BOOST_STATIC_ASSERT((boost::is_base_of<Foo, Bar>::value));
此外,在Boost的is_virtual_base_of
中有一个班级type_traits
,也许这就是你要找的东西。
答案 1 :(得分:5)
这是一个解决方案,用于重定向编译器以执行某些操作,具体取决于该类是否是另一个的子类。
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"
}
如果你想知道增强是如何做到的。如果你上课了 基础和类派生然后以下成立。
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。
进行测试答案 2 :(得分:3)
首先,你的代码正在执行指针的sizeof而不是取消引用的指针,因此即使gcc没有抱怨它也不会起作用。
其次,sizeof技巧必须使用0的强制转换,而不是实际的指针或对象 - 这可以保证零开销,并且在你正确执行之前它不会编译。
第3,你需要声明2个模板化类或结构一个只从D派生,另一个派生自D和虚拟B,然后将0转换为它们的指针,取消引用它们然后取出sizeof。
第4页 - 你有没有什么大的理由试图用static_cast来政治上正确而不是直接投在这里?编译器总是会从中推断出你正在寻找更多的抱怨,在这种情况下你肯定不会。 顺便说一句,你不需要从Alexandrescu那里获取完整的代码 - 只需抓住基本的核心技术:sizeof(*((T*)0))
Alexandrescu非常善于在一招之后进行清理。
哦,请记住,编译器不应该评估sizeof args或实例化未使用的模板化类和结构 - 所以如果它确实那么它是编译器错误,如果你强制它这样做那么它就是你的bug: - )
一旦你有了这个,你需要准确地和积极地定义你的语句“如果指向Derived的指针可以从没有dynamic_cast&lt;&gt;的指针转换为Base”实际上意味着在阶级关系方面 - 只是说“没有操作员/功能Q”并没有明确定义问题,你无法解决你无法定义的事情 - 诚实: - )
所以,只需采取第一个编译的清洁步骤,然后尝试定义您提到的两种情况在现实中会以哪种方式存在差异 - 另一种情况下或另一种情况不会发生的情况。
答案 3 :(得分:2)
你有没有尝试过Loki的SUPERSUBCLASS?
答案 4 :(得分:1)
答案 5 :(得分:1)
在编译时有一个模板黑客可以做到。
首先,您需要创建一个这样的接口类:
template <typename T>
class SomeInterface
{
public:
inline int getSomething() const;
};
template<typename T>
inline int SomeInterface<T>::getSomething() const
{
return static_cast<T const*>(this)->T::getSomething();
}
这个想法是:将this
投射到T
并使用相同的名称和相同的参数调用方法。
正如您所看到的,包装函数是内联的,因此在运行时不会出现性能或调用堆栈开销。
然后创建实现这个接口的类:
class SomeClass : public SomeInterface<SomeClass>
{
friend class SomeInterface<SomeClass>;
public:
int getSomething() const;
};
然后通常只添加派生方法的实现。
这种方式看起来可能并不漂亮,但完全符合这项工作。
答案 6 :(得分:0)
如果您想在编译时知道,可以将派生类作为参数 但是,如果你唯一的东西是Base那么你就不知道它是否指的是任何foo,bar等等。只有在指针转换为Base的情况下才能进行此检查。我认为这是dynamic_cast&lt;&gt;的全部目的。
答案 7 :(得分:-2)
这可能有点幼稚(我在C语言中比在C ++中强得多)所以我可能不明白你想要做什么,但是如果它是你正在谈论的指针,C风格强制转换(例如。(D *)foo
)或C ++等效的reinterpret_cast。话虽如此,这可能非常危险,因为您没有任何运行时检查,因此需要确保您正在进入正确的类型。然后,如果你想要一个简单的方法来检查这是否是一个正确的假设,我们又回到原点。但是,您似乎正在尝试比较上面的指针,它们都是相同的(它们基本上是整数)。据我所知,没有办法在运行时在C ++中确定一个对象的类,包括sizeof,它在编译时工作。基本上,没有办法做你想做的事情(至少不是标准的C ++),但实际的演员不会导致任何问题,只是以不正确的方式使用新的指针。如果你绝对需要这个功能,你可能最好在你的基类中包含一个虚函数来报告它是什么类(最好是枚举值),并在你希望确定是否也可以投射的子类中重载它