views:

989

answers:

3

The original title here was Workaround for SFINAE bug in VS2005 C++

This is tentative use of SFINAE to make the equivalent for the is_pod template class that exists in TR1 (In VS2005 there's no TR1 yet). It should have its value member true when the template parameter is a POD type (including primitive types and structs made of them) and false when it's not (like with non-trivial constructors).

template <typename T> class is_pod
{
  public:

    typedef char Yes;
    typedef struct {char a[2];} No;

    template <typename C> static Yes test(int)
    {
      union {T validPodType;} u;
    }
    template <typename C> static No test(...)
    {
    }
    enum {value = (sizeof(test<T>(0)) == sizeof(Yes))};
};

class NonPOD
{
  public:
    NonPod(const NonPod &);
    virtual ~NonPOD();
};

int main()
{
  bool a = is_pod<char>::value;
  bool b = is_pod<NonPOD>::value;
  if (a) 
    printf("char is POD\n");
  if (b)
    printf("NonPOD is POD ?!?!?\n");
  return 0;
}

The problem is, not only VS 2005 doesn't have TR1, it won't care about the union above (which shouldn't be valid when the template parameter is not a POD), so both a and b evaluate to true.


Thanks for the answers posted below. After reading carefully them (and the code) I realized that what I was trying to do was really a wrong approach. The idea was to combine SFINAE behavior with an adaptation to the template *must_be_pod* (which I found in the book Imperfect C++, but it can be found in another places, too). Actually, this would require a quite particular set of rules for SFINAE, which are not what the standard defines, obviously. This is not really a bug in VS, after all.

+1  A: 

I'm not sure about the way you're trying to do SFINAE here, since is_pod<T>::test(...) will match is_pod<T>::test(0) too. Perhaps if you use a different type instead of 'int' you'd get a better match:

template <typename T> class is_pod
{
  struct my_special_type { };
  public:
    typedef char Yes;
    typedef struct {char a[2];} No;

    template <typename C> static Yes test(my_special_type)
    {
      union {T validPodType;} u;
    }

    template <typename C> static No test(...)
    {
    }
    enum {value = (sizeof(test<T>(my_special_type())) == sizeof(Yes))};
};

You might also want to look at Boost.Enable_if to do your SFINAE for you -- unless you're trying to implement your own library or for some reason.

Dean Michael
I tried to use another type, not the empty struct you suggested, by a char *, to no avail. I just tried to use your example, but it gives the same result (true for both a POD and a non-POD).
Fabio Ceconello
About enable_if, I was considering a similar approach, but this would force me to manually choose the types that are/aren't PODs, and my preference would be to give that burden to the compiler
Fabio Ceconello
I also know that Boost has a template similar to is_pod (in fact, much of TR1 was inspired in Boost and probably this is another example) but I'd rather not add a dependency to a feature that in the future should be part of the standard anyway.
Fabio Ceconello
+1  A: 

This doesn't work with VS2008 either, but I suspect you knew that too. SFINAE is for deducing template arguments for template parameters; you can't really deduce the type of something that reveals the constructor-ness of a type, even though you can create a type that is incompatible with another type (i.e., unions can't use non-POD).

In fact, VS 2008 uses compiler support for traits to implement std::tr1::type_traits.

MSN
+3  A: 

The biggest problem with your approach is you don't do SFINAE here - SFINAE only applies to parameter types and return type here.

However, of all the SFINAE situations in the standard, none applies to your situation. They are

  • arrays of void, references, functions, or of invalid size
  • type member that is not a type
  • pointers to references, references to references, references to void
  • pointer to member of a non-class type
  • invalid conversions of template value parameters
  • function types with arguments of type void
  • const/volatile function type

That's probably why in Boost documentation, there is:

Without some (as yet unspecified) help from the compiler, ispod will never report that a class or struct is a POD; this is always safe, if possibly sub-optimal. Currently (May 2005) only MWCW 9 and Visual C++ 8 have the necessary compiler-_intrinsics.

jpalecek