I recently upgraded GCC to 8.2, and most of my SFINAE expressions have stopped working.
The following is somewhat simplified, but demonstrates the problem:
#include <iostream>
#include <type_traits>
class Class {
public:
template <
typename U,
typename std::enable_if<
std::is_const<typename std::remove_reference<U>::type>::value, int
>::type...
>
void test() {
std::cout << "Constant" << std::endl;
}
template <
typename U,
typename std::enable_if<
!std::is_const<typename std::remove_reference<U>::type>::value, int
>::type...
>
void test() {
std::cout << "Mutable" << std::endl;
}
};
int main() {
Class c;
c.test<int &>();
c.test<int const &>();
return 0;
}
Older versions of GCC (unfortunately I don't remember the exact version I had installed previously) as well as Clang compile the above code just fine, but GCC 8.2 gives an error stating:
: In function 'int main()': :29:19: error: call of overloaded 'test()' is ambiguous c.test(); ^ :12:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value>::type ... = {}]' void test() { ^~~~ :22:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value)>::type ... = {}]' void test() { ^~~~ :30:25: error: call of overloaded 'test()' is ambiguous c.test(); ^ :12:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value>::type ... = {}]' void test() { ^~~~ :22:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value)>::type ... = {}]' void test() {
As is usually the case when different compilers and compiler versions handle the same code differently I assume I am invoking undefined behavior. What does the standard have to say about the above code? What am I doing wrong?
Note: The question is not for ways to fix this, there are several that come to mind. The question is why this doesn't work with GCC 8 - is it undefined by the standard, or is it a compiler bug?
Note 2: Since everyone was jumping on the default void
type of std::enable_if
, I've changed the question to use int
instead. The problem remains.
Note 3: GCC bug report created
gcc 7.3
(and you can see in the assembly that it does the correct thing). – Raynold...
after::type
? – Richytemplate <typename U, void...>
. – Mayonnaisevoid
withint
causes the same effect. The original code uses a memberless enum to prevent accidentally specifying a second template paramter. – Olfactoryvoid
byint
and adding a default fixes the problem, yes. The real question is, what is it about the above code that worked fine before, but doesn't work any more with GCC 8? – Olfactoryvoid...
is legal for and only for empty parameter packs, which is exactly what is desired here. Even if it were illegal, replacing the defaultvoid
withint
in thestd::enable_if
doesn't change anything about the question. – Olfactoryf
in your second example only exists for a constant template parameter. – Olfactoryf<int>
is called! – Exhalationf<int>
results in an error, sincef
only exists for e.g.f<const int>
. Also, the non-typevoid...
template parameter pack is not the problem - feel free to replace thestd::enable_if
type fromvoid
to e.g.int
. – Olfactoryf<int>()
succeeds. Look: godbolt.org/g/RgwSVJ – Exhalation