[英]strange behaviour of operator && in constexpr context
以下代码尝试根据参数包中传递的最后一个参数做出编译时决策。 它包含一个比较,如果参数包 arguments 的数量 > 0,然后尝试获取它的最后一个元素。 但是,构造的元组是在一个无效索引处访问的,该索引应该大于最大元组索引(如static_assert
所示)。
如果我做cnt-1
那怎么可能?
#include <cstdio>
#include <concepts>
#include <utility>
#include <tuple>
template <typename... Args>
auto foo(Args&&... args)
{
auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
constexpr std::size_t cnt = sizeof...(Args);
if constexpr (cnt > 0 && std::same_as<std::remove_cvref_t<std::tuple_element_t<cnt-1, decltype(tuple)>>, int>) {
printf("last is int\n");
} else {
printf("last is not int\n");
}
}
int main()
{
foo(2);
foo();
}
错误:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/tuple: In instantiation of 'struct std::tuple_element<18446744073709551615, std::tuple<> >':
<source>:13:25: required from 'auto foo(Args&& ...) [with Args = {}]'
<source>:24:8: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/tuple:1357:25: error: static assertion failed: tuple index must be in range
1357 | static_assert(__i < sizeof...(_Types), "tuple index must be in range");
| ~~~~^~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/tuple:1357:25: note: the comparison reduces to '(18446744073709551615 < 0)'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/tuple:1359:13: error: no type named 'type' in 'struct std::_Nth_type<18446744073709551615>'
1359 | using type = typename _Nth_type<__i, _Types...>::type;
| ^~~~
阻止 rhs 被评估(在运行时计算其值)的短路不会阻止它被实例化(将模板 arguments 替换为模板,检查它们的有效性,所有这些都在编译时进行)。
我没有看到它不能按您期望的方式工作的任何特殊原因,它只是没有添加到语言中(还)。
正如@wohlstad 所说, if constexpr
是解决方案:
if constexpr (cnt > 0)
{
if constexpr (std::same_as<std::remove_cvref_t<std::tuple_element_t<cnt - 1, decltype(tuple)>>, int>)
{
...
第一个if
必须是 constexpr,而第二个只能是(在您的场景中)。
您可以强制编译器仅在cnt > 0
时评估第二个条件,使用if constexpr
(自 c++17 起可用)并分成 2 个嵌套的if
s:
#include <cstdio>
#include <concepts>
#include <utility>
#include <tuple>
template <typename... Args>
auto foo(Args&&... args)
{
auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
constexpr std::size_t cnt = sizeof...(Args);
//-----vvvvvvvvv---------
if constexpr (cnt > 0)
{
if (std::same_as<std::remove_cvref_t<std::tuple_element_t<cnt - 1, decltype(tuple)>>, int>) {
printf("last is int\n");
}
else {
printf("last is not int\n");
}
}
}
int main()
{
foo(2);
foo();
}
Output:
last is int
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.