19.4 Enumeration Classification with Overload Resolution

Ru-Brd

Overload resolution is the process that selects among various functions with a same name based on the types of their arguments. As shown shortly, we can determine the outcome of a case of overload resolution without actually evaluating a function call. This is useful to test whether a particular implicit conversion exists. The implicit conversion that interests us particularly is the conversion from an enumeration type to an integral type: It allows us to identify enumeration types.

Explanations follow the complete implementation of this technique:

  // types/type7.hpp  struct SizeOverOne { char c[2]; };  template<typename T,           bool convert_possible = !CompoundT<T>::IsFuncT &&                                   !CompoundT<T>::IsArrayT>  class ConsumeUDC {    public:      operator T() const;  };  // conversion to function types is not possible  template <typename T>  class ConsumeUDC<T, false> {  };  // conversion to  void  type is not possible  template <bool convert_possible>  class ConsumeUDC<void, convert_possible> {  };  char enum_check(bool);  char enum_check(char);  char enum_check(signed char);  char enum_check(unsigned char);  char enum_check(wchar_t);  char enum_check(signed short);  char enum_check(unsigned short);  char enum_check(signed int);  char enum_check(unsigned int);  char enum_check(signed long);  char enum_check(unsigned long);  #if LONGLONG_EXISTS    char enum_check(signed long long);    char enum_check(unsigned long long);  #endif  // LONGLONG_EXISTS   // avoid accidental conversions from  float  to  int  char enum_check(float);  char enum_check(double);  char enum_check(long double);  SizeOverOne enum_check(...);  // catch all  template<typename T>  class IsEnumT {    public:      enum { Yes = IsFundaT<T>::No &&                   !CompoundT<T>::IsRefT &&                   !CompoundT<T>::IsPtrT &&                   !CompoundT<T>::IsPtrMemT &&                   sizeof(enum_check(ConsumeUDC<T>()))==1 };      enum { No = !Yes };  }; 

At the heart of our device is a sizeof expression applied to a function call. It results in the size of the return type of the selected function. Hence, overload selection rules are applied to resolve the call to enum_check() , but no definition of the function is needed because the function is not actually called. In this case, enum_check() returns a char , which has size 1 if the argument is convertible to an integral type. All other types are covered by an ellipsis function, but passing an argument "by ellipsis" is the least desirable from an overload resolution point of view. The return type of the ellipsis version of enum_check() was created specifically to ensure it has a size larger than one byte. [1]

[1] A type like double would almost surely work in practice, but in theory such a type may have size "one byte." An array type cannot be used as a return type, so we encapsulated one in a structure.

The argument for the call to enum_check() must be created carefully . First, note that we don't actually know how a T can be constructed . Perhaps a special constructor must be called? To resolve this problem, we can declare a function that returns a T and create an argument by calling that function instead. Because we are in a sizeof expression, we don't actually need to define the function. Perhaps more subtle is the fact that overload resolution could select an enum_check() declaration for an integral type if the argument has a class type T , but that class type defines a user -defined conversion (sometimes also called UDC ) function to an integral type. This problem is solved by actually forcing a user-defined conversion to T using the ConsumeUDC template. The conversion operator also takes care of creating the argument of type T . The expression for the call to enum_check() is thus analyzed as follows (see Appendix B for a detailed overview of overload resolution):

  • The original argument is a temporary ConsumeUDC<T> object.

  • If T is a fundamental integral type, the conversion operator is relied on to create a match with an enum_check() that takes type T as its second argument.

  • If T is an enumeration type, the conversion operator is relied on to enable conversion to T , and type promotion is invoked to match an enum_check() that takes an integral type (typically, enum_check(int,int) ).

  • If T is a class type with a conversion operator to an integral type, the conversion operator cannot be considered because only one user-defined conversion can be invoked for a match and we would first have to use another such conversion from ConsumeUDC<T> to T .

  • No other type T could be made to match an integral type, so the ellipsis version of enum_check() is selected.

Finally, because we want to identify only enumeration types and not fundamental or pointer types, we use the IsFundaT and CompoundT types developed earlier to exclude those from the set of types that cause IsEnumT<T>::Yes to be nonzero.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net