为什么我需要在递归中使用参数包 [duplicate]

Why do I need constexpr in recursion with parameter packs [duplicate]

提问人:koegl 提问时间:11/16/2023 更新时间:11/16/2023 访问量:80

问:

我有一个简单的递归函数来打印参数包中的每个参数

#include <iostream>

template<typename T, typename... Args>
void printArgs(T first, Args... rest) {
    std::cout << first << " ";
    
    if constexpr (sizeof...(rest) > 0) { // doesn't compile without constexpr
        printArgs(rest...);
    } else {
        return;
    }
}

int main() {
    printArgs(1, 2, "hello");

    return 0;
}

问1:为什么我需要在程序中编译?
Q2: 条件不应该是吗?因为如果大小是,我再打电话,那不会是空的吗?(可以是空的吗?
constexprifsizeof...(rest) > 11printArgsrest

我看到了类似的问题,比如优化的“constexpr if”与“if”——为什么需要“constexpr”?,但我不明白这些答案与我的情况有什么关系。

C++ 模板 if-constexpr

评论

2赞 Daniel Langr 11/16/2023
对于普通(non-constexpr),第一个分支也需要为空参数包进行编译。但是没有可调用的函数定义。ifprintArgs()
0赞 koegl 11/16/2023
@DanielLangr为什么需要为空包编译呢?
2赞 Pepijn Kramer 11/16/2023
想象一下,如果你这样做会发生什么,那么会发生什么,那么它将是空的,只有第一个才有价值。不需要分支printArgs(1)Args... restelse
1赞 StoryTeller - Unslander Monica 11/16/2023
因为运行时有一个常规的控制流,所以在运行时,该流必须存在。即从某物编译而成。if
0赞 user12002570 11/16/2023
if constexpr 就是要确保只实例化 if 的一个分支。

答:

-1赞 Pepijn Kramer 11/16/2023 #1

此外,您不必使用显式递归,从 C++17 开始,您可以使用折叠表达式。

以下代码将输出1, 2, hello

#include <iostream>

template<typename First, typename... Args>
void printArgs(First&& first, Args&&... rest) 
{
    std::cout << std::forward<First>(first);

    //https://en.cppreference.com/w/cpp/language/fold
    // the part before ,... will be repeated/expanded
    // each time with one argument
    ((std::cout << ", " << std::forward<Args>(rest)),...);
}

int main() 
{
    printArgs(1, 2, "hello");

    return 0;
}
5赞 463035818_is_not_an_ai 11/16/2023 #2

我将从第二个问题开始,因为这个问题的答案也解释了问题 1。

Q2:条件不应该是吗?因为如果大小是 1,我再次调用,那么不会是空的吗?(它是空的可以吗?sizeof...(rest) > 1printArgsrest

你的错误是计算.但是如果你调用,那么 是从 和 是空的,并且是 .可以使用 1 () 加零或更多 () 参数来调用函数。Tsizeof...(rest)printArgs(onlyOne)TonlyOneArgssizeof...(rest)0T firstArgs... rest

Q1:为什么在if中需要constexpr才能编译程序?

如果删除 ,则会出现以下错误constexpr

<source>: In instantiation of 'void printArgs(T, Args ...) [with T = const char*; Args = {}]':
<source>:8:18:   recursively required from 'void printArgs(T, Args ...) [with T = int; Args = {const char*}]'
<source>:8:18:   required from 'void printArgs(T, Args ...) [with T = int; Args = {int, const char*}]'
<source>:15:14:   required from here
<source>:8:18: error: no matching function for call to 'printArgs()'
    8 |         printArgs(rest...);
      |         ~~~~~~~~~^~~~~~~~~
<source>:4:6: note: candidate: 'template<class T, class ... Args> void printArgs(T, Args ...)'
    4 | void printArgs(T first, Args... rest) {
      |      ^~~~~~~~~
<source>:4:6: note:   template argument deduction/substitution failed:
<source>:8:18: note:   candidate expects at least 1 argument, 0 provided
    8 |         printArgs(rest...);
      |         ~~~~~~~~~^~~~~~~~~

因为在最后一次递归中,只有 1 个参数被传递给并且没有元素。在这种情况下,编译失败,因为(见上文)您的函数只能用 1 或更多调用。 导致 false 分支被丢弃,并且您调用的部分永远不会实例化。printArgsrestprintArgs(rest...)if constexprprintArgs()