c++ constexpr上下文中运算符&&奇怪行为

8wigbo56  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(160)

下面的代码试图根据参数包中传递的最后一个参数做出编译时决策。如果参数包参数的数量大于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;
      |             ^~~~
wqsoz72f

wqsoz72f1#

Short-circuiting阻止对RHS进行 * 求值 *(在运行时计算其值),但不会阻止对它进行 * 示例化 *(将模板参数替换为模板,检查其有效性,所有这些都在编译时进行)。
我看不出有什么特别的原因让它不能像你期望的那样工作,它只是还没有被添加到语言中。
正如@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,而第二个只 * 应该 *(在您的场景中)。

jum4pzuy

jum4pzuy2#

只有当cnt > 0使用if constexpr(c++17起可用)并分成2个嵌套的if时,才能强制编译器计算第二个条件:

#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();
}

输出:

last is int

相关问题