开发者

C++: type_info to distinguish types

I know that compilers have much freedom in implementing std::type_info functions' behavior.

I'm thinking about using it to compare object types, so I'd like to be sure that:

  1. std::type_info::name must return two different strings for two different types.

  2. std::type_info::before must say that Type1 is before 开发者_如何学GoType2 exclusive-or Type2 is before Type1.

    // like this:
    typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
    
  3. Two different specialization of the same template class are considered different types.

  4. Two different typedef-initions of the same type are the same type.

And finally:

  • Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?

  • How fast are operator==, operator!= and before on most common compilers? I guess they should only compare a value. And how fast is typeid?

  • I've got a class A with a virtual bool operator==( const A& ) const. Since A has got many subclasses (some of which are unknown at compile time), I'd overload that virtual operator in any subclass B this way:

    virtual bool operator==( const A &other ) const {
      if( typeid(*this) != typeid(other) ) return false;
      // bool B::operator==( const B &other ) const // is defined for any class B
      return operator==( static_cast<B&>( other ) );
    }
    

    Is this an acceptable (and standard) way to implement such operator?


After a quick look at the documentation, I would say that :

  1. std::type_info::name always returns two different strings for two different types, otherwise it means that the compiler lost itself while resolving types and you shouldn't use it anymore.

  2. Reference tells : "before returns true if the type precedes the type of rhs in the collation order. The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order." You therefore have the guarantee that no types has the same rank in the collation order.

  3. Each instantiation of a template class is a different type. Specialization make no exceptions.

  4. I don't really understand what you mean. If you mean something like having typedef foo bar; in two separate compilation units and that bar is the same in both, it works that way. If you mean typedef foo bar; typedef int bar;, it doesn't work (except if foo is int).

About your other questions :

  • You should store references to std::type_info, of wrap it somehow.
  • Absolutely no idea about performance, I assume that comparison operators have constant time despite of the type complexity. Before must have linear complexity depending on the number of different types used in your code.
  • This is really strange imho. You should overload your operator== instead of make it virtual and override it.


Standard 18.5.1 (Class type_info) :

The class type_info describes type information generated by the implementation. Objects of this class effectively store a pointer to a name for the type, and an encoded value suitable for comparing two types for equality or collating order. The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs.

From my understanding :

  1. You don't have this guarantee regarding std:type_info::name. The standard only states that name returns an implementation-defined NTBS, and I believe a conforming implementation could very well return the same string for every type.
  2. I don't know, and the standard isn't clear on this point, so I wouldn't rely on such behavior.
  3. That one should be a definite 'Yes' for me
  4. That one should be a definite 'Yes' for me

Regarding the second set of questions :

  • No, you cannot store a type_info. Andrei Alexandrescu proposes a TypeInfo wrapper in its Modern C++ Design book. Note that the objects returned by typeid have static storage so you can safely store pointers without worrying about object lifetime
  • I believe you can assume that type_info comparison are extremely efficient (there really isn't much to compare).


You can store it like this.

class my_type_info
{
public:
     my_type_info(const std::type_info& info) : info_(&info){}
     std::type_info get() const { return *info_;}
private:
     const std::type_info* info_;
};

EDIT:

C++ standard 5.2.8.

The result of a typeid expression is an lvalue of static type const std::type_info...

Which means you can use it like this.

my_type_info(typeid(my_type));

The typeid function returns an lvalue (it is not temporary) and therefore the address of the returned type_info is always valid.


The current answers for questions 1 and 2 are perfectly correct, and they're essentially just details for the type_info class - no point in repeating those answers.

For questions 3 and 4, it's important to understand what precisely is a type in C++, and how they relate to names. For starters, there are a whole bunch of predefined types, and those have names: int, float, double. Next, there are some constructed types that do not have names of their own: const int, int*, const int*, int* const. There are function types int (int) and function pointer types int (*)(int).

It's sometimes useful to give a name to an unnamed type, which is possible using typedef. For instance, typedef int* pint or typedef int (*pf)(int);. This introduces a name, not a new type.

Next are user-defined types: structs, classes, unions. There's a good convention to give them names, but it's not mandatory. Don't add such a name with typedef, you can do so directly: struct Foo { }; instead of typedef struct {} Foo;. It's common to have class definitions in headers, which end up\ in multiple translation units. That does mean the class is defined more than once. This is still the same type, and therefore you aren't allowed to play tricks with macros to change the class member definitions.

A template class is not a type, it's a recipe for types. Two instantiations of a single class template are distinct types if the template arguments are different types (or values). This works recursively: Given template <typename T> struct Foo{};, Foo<Foo<int> > is the same type as Foo<Foo<Bar> > if and only if Bar is another name for the type int.


Type_info is implementation defined so I really wouldn't rely on it. However, based on my experiences using g++ and MSVC, assumptions 1,3 and 4 hold... not really sure about #2.

Is there any reason you can't use another method like this?

template<typename T, typename U>
struct is_same       { static bool const result = false; };

template<typename T>
struct is_same<T, T> { static bool const result = true;  };

template<typename S, typename T>
bool IsSame(const S& s, const T& t) {   return is_same<S,T>::result; }


Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?

This is why std::type_index exists -- it's a wrapper around a type_info & that is copyable and compares (and hashes) by using the underlying type_info operations

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜