How to know if the argument that is passed to the

2019-02-15 08:39发布

问题:

I want to define an operator<< for all enums, to cout the value and print that it is an enum like this:

code:

enum AnyEnum{A,B,C};
AnyEnum enm = A;
cout << enm <<endl;

output:

This is an enum which has a value equal to 0

I know a way of doing this with Boost library by using is_enum struct. But I don’t understand how it works. So that's why, in general, I am interested how to identify if the veriable is a class type, union type or an enum (in compile time).

回答1:

Determining class types you could use the fact that member pointers exist

template<typename A, typename B>
struct issame { };

template<typename A>
struct issame<A, A> { typedef void type; };

template<typename> struct tovoid { typedef void type; };

template<typename T, typename = void>
struct isclass { static bool const value = false; };

template<typename C>
struct isclass<C, typename tovoid<int C::*>::type> {
  static bool const value = true;
};

You cannot detect the difference of an union and a non-union class. At least I don't know how, and boost doesn't know either.

I think detecting enums could work by making sure T isn't a class, function or integral type, and then trying to assign to an integral type. You could

template<typename E, typename = void> 
struct isenum { 
  struct No { char x; };
  struct Yes { No n1; No n2; };

  struct nullsink {};
  static No checkI(nullsink*); // accept null pointer constants
  static Yes checkI(...);

  static Yes checkE(int);
  static No checkE(...);

  static bool const value = (sizeof(checkI(E())) == sizeof(Yes)) && 
                            (sizeof(checkE(E())) == sizeof(Yes));
};

// class
template<typename E>
struct isenum<E, typename tovoid<int E::*>::type> {
  static bool const value = false;
};

// reference
template<typename R>
struct isenum<R&, void> {
  static bool const value = false;
};

// function (FuntionType() will error out).
template<typename F>
struct isenum<F, typename issame<void(F), void(F*)>::type> {
  static bool const value = false;
};

// array (ArrayType() will error out)
template<typename E>
struct isenum<E[], void> {
  static bool const value = false;
};
template<typename E, int N>
struct isenum<E[N], void> {
  static bool const value = false;
};

Quick & dirty test (works on GCC/clang/comeau):

enum A { };
struct B { };
typedef int &C;
typedef void D();
typedef int E;
typedef long F;
typedef int const G;
typedef int H[1];

template<typename T, bool E>
struct confirm { typedef char x[(T::value == E) ? 1 : -1]; };

int main() {
  confirm< isenum<A>, true >();
  confirm< isenum<B>, false >();
  confirm< isenum<C>, false >();
  confirm< isenum<D>, false >();
  confirm< isenum<E>, false >();
  confirm< isenum<F>, false >();
  confirm< isenum<G>, false >();
  confirm< isenum<H>, false >();
}


回答2:

I am interested how to identify if the veriable is a class type, union type or an enum (in compile time).

boost::type_traits

Even C++ TR1 has got a <type_traits> header to support that functionality. In C++0x everything's gonna be a lot better.

For example the following machinery makes use of SFINAE to check whether the argument passed is a class type:

template<typename T>struct Check_If_T_Is_Class_Type
{
    template<typename C> static char func (char C::*p);
    template<typename C> static long func (...);
    enum{val = CHECKER(func,Check_If_T_Is_Class_Type)};
};

The MACRO CHECKER is

#define CHECKER(func_name,class_name) \
sizeof(class_name<T>::template func_name<T>(0)) == 1

To understand how type_traits work you need to have some basic knowledge of templates including template metaprogramming and SFINAE.



回答3:

This is usually done with compiler hooks. The compiler has special functions that "fill" the template with the apropriate value (at least in C++0x where type_traits has been standardized). For instance the is_pod trait uses the __is_pod compiler hook under VC 10 to get the apropriate information.



回答4:

its not possible to know the variable type at compile time.