The following example fails in all major compilers: clang
, gcc
and visual studio
.
I would like to know what's wrong with it, it seems to be to be very simple:
if sizeof...(TYPES) == 2
then it should exclude one overload and accept the other one, if 0, 1 or greater than 2 then it should accept the first overload
and exclude the 2nd one.
Why doesn't this work this way?
#include <iostream>
#include <type_traits>
template <typename... TYPES>
struct Test {
template <std::enable_if_t<(sizeof...(TYPES) != 2), int> = 0>
Test() {
std::cout << "A\n";
}
template <std::enable_if_t<(sizeof...(TYPES) == 2), int> = 0>
Test() {
std::cout << "B\n";
}
};
int
main() {
Test<int, int> t1;
Test<int, int, int> t2;
return 0;
}
clang
error:
In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtr1common:54:40: error: no type named 'type' in 'std::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Test, _Ty>::type;
^~~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:6:17: note: in instantiation of template type alias 'enable_if_t' requested here
template <std::enable_if_t<(sizeof...(TYPES) != 2), int> = 0>
^
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:20:17: note: in instantiation of template class 'Test<int, int>' requested here
Test<int, int> t1;
^
In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtr1common:54:40: error: no type named 'type' in 'std::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Test, _Ty>::type;
^~~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:11:17: note: in instantiation of template type alias 'enable_if_t' requested here
template <std::enable_if_t<(sizeof...(TYPES) == 2), int> = 0>
^
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:21:22: note: in instantiation of template class 'Test<int, int, int>' requested here
Test<int, int, int> t2;
^
2 errors generated.
[Finished in 0.8s]
gcc
error:
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp: In instantiation of 'struct Test<int, int>':
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:20:17: required from here
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:7:2: error: no type named 'type' in 'struct std::enable_if<false, int>'
Test() {
^~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp: In instantiation of 'struct Test<int, int, int>':
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:21:22: required from here
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:12:2: error: no type named 'type' in 'struct std::enable_if<false, int>'
Test() {
^~~~
[Finished in 2.1s]
visual studio
error:
1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(160,1): error C2938: 'std::enable_if_t<false,int>' : Failed to specialize alias template
1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(159): message : see reference to alias template instantiation 'std::enable_if_t<false,int>' being compiled
1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(208): message : see reference to class template instantiation 'Test<int,int,int>' being compiled
1>Done building project "A++.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
This does not work since your template-member-functions SFINAE on the template parameter from the class. SFINAE only works on types and expressions in the immediate context, here that means template parameter that belong to the constructors template signature.
To get around this we can introduce a new template parameter in the constructor and give it a default value that depends on the class-template.
#include <iostream>
#include <type_traits>
template <typename... TYPES>
struct Test {
template <std::size_t i = sizeof...(TYPES), std::enable_if_t<i != 2, int> = 0>
Test() {
std::cout << "A\n";
}
template <std::size_t i = sizeof...(TYPES), std::enable_if_t<i == 2, int> = 0>
Test() {
std::cout << "B\n";
}
};
int
main() {
Test<int, int> t1;
Test<int, int, int> t2;
return 0;
}