2

このコードで、typename T (Test(...) 関数内) が Bar 型ではなく Foo 型と評価されるのはなぜですか? また、バー型になるように変更するにはどうすればよいですか?

#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

テンプレートはコンパイル時に作成されるため、渡されたランタイム タイプを判別できません。

とにかく、値で渡しているので、元の type に関する情報を失うオブジェクトに変換してスライスすることになります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 に答える