4

I am trying to make scoped enumerations in my program comparable to underlying type but the following code does not work. Is it because of poor C++11 standard support in the compiler I am using (VC11) or is it because the code breaks some rules from C++11 standard? In latter case which rules exactly are being broken (references to specific standard clauses are welcome)?

#include <type_traits>
enum class Test: short int { A,B,C };
template<typename E> bool operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}
template<typename E> bool operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}
int main()
{
    short int x = 123;
    x != Test::B; // compilation error
}

Here is why I think my code should be C++11 compliant. A quote from C++11 standard (14.8.3.1):

For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, >for a given function template, argument deduction fails, no such function is added to the set of >candidate functions for that template.

EDIT. My code is not C++11 compliant (thanks Vaughn Cato and Andy Prowl for explanation). The alternative working code is provided in Andy Prowl's answer.

P.S. After all I ended up making unscoped enums scoped using namespaces:

namespace Test_ {
    enum Test { A,B,C };
};
using Test_::Test;

namespace Test2_ {
    enum Test2 { Z,Y,B };
};
using Test2_::Test2;
PowerGamer
  • 2,106
  • 2
  • 17
  • 33

2 Answers2

4

You could use SFINAE to rule out the instantiation of the signature of your comparison operators (and therefore the instantiation of std::underlying_type<T>) when the corresponding argument is not an enumeration:

#include <type_traits>

template<typename E, 
    typename std::enable_if<std::is_enum<E>::value>::type* = nullptr>
bool operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

template<typename E, 
    typename std::enable_if<std::is_enum<E>::value>::type* = nullptr>
bool operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

Here is a live example.

EDIT:

Since VC11 seems to lack support for default arguments on template parameters of a function template, here is an alternative solution:

template<typename E>
typename std::enable_if<std::is_enum<E>::value, bool>::type
operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

template<typename E>
typename std::enable_if<std::is_enum<E>::value, bool>::type
operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

And a live example of course.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Not at all, since I was not asking for a possible workaround for the lack of C++11 support in a specific compiler, but rather for a confirmation if my code is C++11 compliant or not (your code does not work in VC11 anyway as VC11 lacks support for default template arguments for function templates). BUT your mention of SFINAE did help me better understand my own code I have written :) Now I think it should be C++11 compliant (still would like a confirmation from a knowledgable person though), and the reason it does not work in VC11 is because it lacks support for expression SFINAE. – PowerGamer Mar 17 '13 at 13:32
  • @PowerGamer: Your code won't work on any compiler ([see here](http://liveworkspace.org/code/4cvdpz$60) to get convinced), for precisely the reasons I explained in the previous comments (which I deleted, since you deleted your request for clarification). It's not about lack of support, your code simply won't work. Probably VC11 lacks support for default arguments on template parameters, and that's why *my* solution doesn't work on VC11. I didn't know that. But it can be worked around. Let me edit. – Andy Prowl Mar 17 '13 at 13:36
  • 1
    I still do not see why my code is not C++11 compliant. Shouldn't compiler simply throw away overloaded template specialization that produces error (see the quote from the standard in my post). Also, liveworkspace.org does not work in IE10 I use. And you edited code works in VC11. My deleting of comments is due to them not being fully finished as I have not expected Enter key to submit them. – PowerGamer Mar 17 '13 at 14:03
  • 1
    @PowerGamer: Not if the error is produced in a non-immediate context, which is the case of your `std::underlying_type::type`. This is not so simple to explain in a comment, but you could read [this Q&A](http://stackoverflow.com/questions/15260685/what-is-exactly-the-immediate-context-mentioned-in-the-c11-standard-for-whic) to figure it out. – Andy Prowl Mar 17 '13 at 14:07
1

Section 14.8.2 paragraph 8 of the C++11 standard states:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [ Note: Access checking is done as part of the substitution process. — end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]

In your case, instantiating underlying_type causes a failure, but not in the immediate context, so it is not a type deduction failure, so SFINAE does not apply.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132