enable_if 中参数包的 sizeof... 问题
Problem with sizeof... of parameter pack in enable_if
以下示例在所有主要编译器中均失败:clang
、gcc
和 visual studio
。
我想知道这是怎么回事,好像很简单:
if sizeof...(TYPES) == 2
那么它应该排除一个重载并接受另一个,如果是 0、1 或大于 2 那么它应该接受第一个重载
并排除第二个。
为什么这样不行?
#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
错误:
In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.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 Studio19\Community\VC\Tools\MSVC.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.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
错误:
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
错误:
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 ==========
这不起作用,因为您的模板成员函数 SFINAE 在 class 的模板参数上。 SFINAE 仅适用于直接上下文中的类型和表达式,这里指的是属于构造函数模板签名的模板参数。
为了解决这个问题,我们可以在构造函数中引入一个新的模板参数,并为其指定一个取决于 class-模板的默认值。
#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;
}
自 C++20 起,您可以使用 requires
丢弃不需要的构造函数:
template <typename... TYPES>
struct Test {
Test() requires(sizeof...(TYPES) != 2) { std::cout << "A\n"; }
Test() requires(sizeof...(TYPES) == 2) { std::cout << "B\n"; }
};
即使在那种情况下,一个简单的 if
(constexpr
) 就可以完成工作:
template <typename... TYPES>
struct Test {
Test()
{
if constexpr (sizeof...(TYPES) != 2) {
std::cout << "A\n";
} else {
std::cout << "B\n";
}
}
};
以下示例在所有主要编译器中均失败:clang
、gcc
和 visual studio
。
我想知道这是怎么回事,好像很简单:
if sizeof...(TYPES) == 2
那么它应该排除一个重载并接受另一个,如果是 0、1 或大于 2 那么它应该接受第一个重载
并排除第二个。
为什么这样不行?
#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
错误:
In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.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 Studio19\Community\VC\Tools\MSVC.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.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
错误:
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
错误:
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 ==========
这不起作用,因为您的模板成员函数 SFINAE 在 class 的模板参数上。 SFINAE 仅适用于直接上下文中的类型和表达式,这里指的是属于构造函数模板签名的模板参数。
为了解决这个问题,我们可以在构造函数中引入一个新的模板参数,并为其指定一个取决于 class-模板的默认值。
#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;
}
自 C++20 起,您可以使用 requires
丢弃不需要的构造函数:
template <typename... TYPES>
struct Test {
Test() requires(sizeof...(TYPES) != 2) { std::cout << "A\n"; }
Test() requires(sizeof...(TYPES) == 2) { std::cout << "B\n"; }
};
即使在那种情况下,一个简单的 if
(constexpr
) 就可以完成工作:
template <typename... TYPES>
struct Test {
Test()
{
if constexpr (sizeof...(TYPES) != 2) {
std::cout << "A\n";
} else {
std::cout << "B\n";
}
}
};