2

在这段代码中,为什么 typename T(在 Test(...) 函数中)评估为类型 Foo 而不是类型 Bar?我怎样才能改变它,使它变成 Bar 类型?

#include <typeinfo>
using namespace std;

struct Foo
{
    virtual ~Foo() { }
};

struct Bar : public Foo
{
};

template<typename T>
bool Test(T foo)
{
    return (typeid(foo) == typeid(Bar));
}

int main()
{
    Bar bar;
    Foo *foo = &bar;
    bool THIS_IS_TRUE = (typeid(*foo) == typeid(Bar));
    bool WHY_ISNT_THIS = Test(*foo);
    return 0;
}
4

4 回答 4

6

模板是基于静态类型而不是动态类型推导出来的。代码是根据编译器在编译时知道的内容静态绑定和创建的,与运行时周围的类型无关。此外,当您*foo按值传递时,无论如何您都在对对象进行切片。

于 2012-12-30T18:06:51.513 回答
4

模板是在编译时完成的,因此它们无法确定您传递给它们的运行时类型。

无论如何,由于您是按值传递的,因此您将转换和切片到对象,这将丢失有关原始类型的信息Bar。请尝试参考:

template<typename T>
bool Test(T& foo)
{
    return (typeid(foo) == typeid(Bar));
}

T还是会被推到Foo,但typeid(foo)应该是Bar

于 2012-12-30T18:06:55.990 回答
1

您必须保留指针或通过引用传递值,不能通过副本传递变量(按值传递)。

尝试这个 :

template<typename T>
bool Test(T foo)
{
    return (typeid(*foo) == typeid(Bar));
}

int main()
{
    Bar bar;
    Foo *foo = &bar;
    bool THIS_IS_TRUE = (typeid(*foo) == typeid(Bar));
    bool WHY_ISNT_THIS = Test(foo);
    return 0;
 }
于 2012-12-30T18:04:59.457 回答
0

你想做什么是行不通的。这是另一种方法,我基本上构建自己的类型基础设施:

#include <functional>
struct Base {
protected:
  virtual ~Base() {}
  Base() {}
public:
  virtual size_t SizeRequired() const = 0;
  virtual std::function<Base*(unsigned char* buffer)> Constructor() const = 0;
  // note that standard delete is not standards compliant, because the memory
  // was allocated as an array of unsigned char's
  void SelfDelete() {
    SelfDestroy();
    delete[] reinterpret_cast<unsigned char*>(this);
  }

  // you can also create these things in other storage locations:
  void SelfDestroy() {
    this->~Base();
  }
  static void Delete( Base* b ) {
    if (b)
      b->SelfDelete();
  }
  std::function<Base*()> AllocateAndConstructor() const {
    size_t required = this->SizeRequired();
    auto constructor = this->Constructor();
    return [required, constructor]()->Base* {
      unsigned char* buff = new unsigned char[ required ];
      return constructor( buff );
    };
  }
};

// CRTP class (google CRTP if you are confused what I'm doing)
template<typename Child>
struct BaseHelper: Base {
  static Base* Construct(unsigned char* buffer) {
    return new(buffer) Child();
  }
  static Base* Create() {
    unsigned char* buff = new unsigned char[ sizeof(Child) ];
    return Construct( buff );
  };
  virtual size_t SizeRequired() const {
    return sizeof(Child);
  }
  virtual std::function<Base*(unsigned char* buffer)> Constructor() const {
    return &BaseHelper<Child>::Construct;
  }
};

// use:

struct Bar: BaseHelper<Bar> {
};
struct Foo: BaseHelper<Foo> {
};

Base* test(Base* b) {
  auto creator = b->AllocateAndConstructor();
  return creator();
}
#include <iostream>
int main() {
  Base* b = Bar::Create(); // creates a Bar
  Base* b2 = test(b); // creates another Bar, because b is a Bar
  Base* f = Foo::Create(); // creates a Foo
  Base* f2 = test(f); // creates another Foo, because f is a Foo
  std::cout << (typeid(*b) == typeid(*b2)) << " == 1\n";
  std::cout << (typeid(*f) == typeid(*f2)) << " == 1\n";
  std::cout << (typeid(*f) == typeid(*b)) << " == 0\n";
  Base::Delete(b);
  Base::Delete(b2);
  Base::Delete(f);
  Base::Delete(f2);
  Base::Delete(0); // does not crash
};

这意味着 Base 的每个运行时实例都带有构造 Base 实例的能力。覆盖 new 和 delete 可能比使用上述替换创建和删除操作更好,并且会让某人在堆栈上创建这些东西。

除非您知道自己在做什么,否则我建议您不要使用这种技术。

然而,有一些相对常见的模式,如克隆和工厂,它们与上述模式接近,但不那么愚蠢。

于 2012-12-30T20:04:53.733 回答