3
votes

Can someone explain the following behaviour (I'm using Visual Studio 2010).
header:

#pragma once
#include <boost\utility\enable_if.hpp>
using boost::enable_if_c;

enum WeekDay {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};

template<WeekDay DAY>
typename enable_if_c< DAY==SUNDAY, bool >::type goToWork()  {return false;}

template<WeekDay DAY>
typename enable_if_c< DAY!=SUNDAY, bool >::type goToWork()  {return true;}

source:

bool b = goToWork<MONDAY>();

compiler this gives

error C2770: invalid explicit template argument(s) for 'enable_if_c<DAY!=6,bool>::type goToWork(void)'  

and

error C2770: invalid explicit template argument(s) for 'enable_if_c<DAY==6,bool>::type goToWork(void)'

But if I change the function template parameter from the enum type WeekDay to int, it compiles fine:

template<int DAY>
typename enable_if_c< DAY==SUNDAY, bool >::type goToWork()  {return false;}

template<int DAY>
typename enable_if_c< DAY!=SUNDAY, bool >::type goToWork()  {return true;}

Also the normal function template specialization works fine, no surprises there:

template<WeekDay DAY>  bool goToWork()          {return true;}
template<>             bool goToWork<SUNDAY>()  {return false;}

To make things even weirder, if I change the source file to use any other WeekDay than MONDAY or TUESDAY, i.e. bool b = goToWork<THURSDAY>(); the error changes to this:

error C2440: 'specialization' : cannot convert from 'int' to 'const WeekDay'  
Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)  

EDIT: Maybe someone could test this with a different compiler (other than Visual Studio 2010) to see if the same thing happens, because it doesn't seem to make any sense

EDIT: I found a new "interesting" aspect of this behaviour. That is if I change the direct comparison of the template parameter with == and != operators into a comparison with a helper struct template, it works fine:

template<WeekDay DAY>
struct Is
{
    static const bool   Sunday = false;
};

template<>
struct Is<SUNDAY>
{
    static const bool   Sunday = true;
};

template<WeekDay DAY>
typename enable_if_c< Is<DAY>::Sunday, bool >::type   goToWork()  {return false;}

template<WeekDay DAY>
typename enable_if_c< !Is<DAY>::Sunday, bool >::type  goToWork()  {return true;}

EDIT: By the way, I made a bug report and this is the answer from Microsoft: "This is a bug that manifests when attempting to promote the non-type template parameter. Unfortunately, given our resource constraints for this release and that a worked-around is available, we will not be able to fix this in the next release of Visual Studio. The work-around is to change the template parameter type to an int."

(I think "this release" refers to Visual Studio 2010)

1

1 Answers

2
votes

Works fine in GCC 4.2.1.

Looks like either VC's template engine is missing comparison operators for enum types, or it sloppily converted the enum to int and then decided to be strict and disallow implicit conversion to int (apparently with an exception for 0 and 1).