两个 C++ 编译器不同意我的“count_args”函数是否是 constexpr

问题描述 投票:0回答:2

这里有一个小函数,可以计算你传入的参数的数量:

#include <iostream>

constexpr static int count_args() {
    return 0;
}

template<typename... SS>
constexpr static int count_args(int &v, SS & ... restargs)
{
    constexpr int rest = count_args(restargs...);
    return rest + 1;
}

int main(int argc, char **argv) {
    int a,b;
    constexpr int count = count_args(a,b);
    std::cerr << "count: " << count << std::endl; // Intended to print 'count: 2'
}

两个不同的编译器为此给出了两个不同的结果。在 G++ 10.3.0 下,我收到此错误消息:

> g++ -std=c++17 -o cx2.exe cx2.cpp
cx2.cpp: In instantiation of 'constexpr int count_args(int&, SS& ...) [with SS = {int}]':
cx2.cpp:15:16: error: 'restargs#0' is not a constant expression
   15 |  constexpr int rest = count_args(restargs...);

但在 G++ 9.4.0 下,它可以按预期编译和运行。有人可以澄清哪个编译器在“做正确的事”吗?这是正确的 constexpr 代码,还是无效代码?

c++ variadic-templates constexpr
2个回答
0
投票

根据个人经验,较旧的 g++ 编译器版本通常在检查某些内容是否为常量表达式时草率。如果某些东西被优化成为一个常量,即使它以前不是,它也会被毫无错误地接受。

这不是 C++11 到 C++20 中的常量表达式。原因是引用必须由常量表达式中可用的东西初始化(函数参数不是)。理由是您不知道

SS & ... restargs
是否都是有效的引用。例如,如果你像
count_args
一样调用
count_args(a, *static_cast<int*>(nullptr));
,它不应该是一个常量表达式。

这在 C++23 中被 P2280R4 改变了,clang 和 gcc 都没有实现,如下所示:https://clang.llvm.org/cxx_status.html#cxx23https://gcc .gnu.org/projects/cxx-status.html。这使得

restargs....
可以在常量表达式中使用的“未知引用”,即使它们的初始值设定项是未知的。


0
投票

bug在这个函数中

template<typename... SS>
constexpr static int count_args(int &v, SS & ... restargs)
{
    constexpr int rest = count_args(restargs...);
    return rest + 1;
}

您通过删除导致编译错误的

constexpr
来修复它。

template<typename... SS>
constexpr static int count_args(int &v, SS & ... restargs)
{
    int rest = count_args(restargs...);
    return rest + 1;
}

这将在 GCC、Clang 和 MSVC 上编译并产生正确的结果。说实话,我不确定这是一个合法的错误、标准缺陷还是其他原因。但是你可以毫无问题地修复它。

我认为问题出在模板函数的静态分析上,无法判断调用

count_args(restargs...)
会产生
constexpr
,因为输入变量不是 constexpr。这通常不应该成为问题,因为返回非 constexpr 变量不会在编译时实例化和调用模板函数时造成问题。

© www.soinside.com 2019 - 2024. All rights reserved.