Reputation: 1590
I have a bunch of functions who all have the same name but have different arguments (some of them are templates, some aren't):
void f(int& a) { ... } // there are a few other arithmetic types as well
void f(bool& a) { ... }
template <class T1, class T2> void f(std::pair<T1, T2>&a) { ... }
template <class T> void f(T& a) { ... }
// ...
In addition to that, I have an enum:
enum MyEnum {
enumVal1, enumVal2
};
I actually thought that if I now call MyEnum e = enumVal1; f(e);
, it would automatically call the overload of MyEnum
's underlying type. It doesn't. Instead, it generates a compiler error.
Now I want to define a template function that catches all enums and calls the appropriate function.
template <class T, std::enable_if_t<std::is_enum<T>{}> * = nullptr>
void f(T &a) { /* cast to std::underlying_type<T> and call f() */ }
Unfortanetely for some reason this creates an ambiguity with the existing f(T& a)
.
How can I solve this problem? The solution has to be valid for all enums (but not for class enums).
I compile my code using gcc 4.9 and clang 3.5.
Upvotes: 3
Views: 1047
Reputation: 154045
The use of SFINAE doesn't provide a better versions for partial ordering but merely determines which overloads are available. That is, for enums there two equally good candidate functions of f()
resulting in an ambiguity. The easiest way to address this ambiguity is to SFINAE both candidates to be for mutually exclusive sets of types:
template <class T, std::enable_if_t<!std::is_enum<T>{}> * = nullptr>
void f(T &a) {
// version not applicable for enums
}
template <class T, std::enable_if_t<std::is_enum<T>{}> * = nullptr>
void f(T &a) {
/* cast to std::underlying_type<T> and call f() */
}
This does not solve your original problem, though, as enumerator are no lvalues. Note that your original problem canb be solved by not taking a T&
as argument but rather taking a T
, a T const&
or possibly a T&&
: enum
s do convert to an underlying type but an enumerator tag is not an lvalue and can't be bound to a non-const
reference.
Unless there is a good reason not to, I'd probably go with just one overload:
template <typename T>
void f(T&& a) {
// ...
}
Upvotes: 6
Reputation:
The functions f
only accept (non const) references:
int main() {
auto e = enumVal1;
f(e);
return 0;
}
Hence f(enumVal1)
fails.
Upvotes: 1