You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数参数包处于非推导上下文时的推导规则与编译器行为疑问

函数参数包处于非推导上下文时的推导规则与编译器行为疑问

当前标准草案中有这样的描述(见某段落的最后一句):

"当函数参数包出现在非推导上下文时,该包的类型永远不会被推导。"

我一直对这句话的含义感到困惑:如果用户既没有显式指定这个参数包的实参,同时它又处于非推导上下文无法被推导,那这不应该直接触发编译错误吗?但实际测试下来,当前编译器都会把这种情况下的参数包当作空包处理,这就让我更懵了。

先看一个从标准草案里简化出来的例子:

template<class T1, class ... Types> void g1(Types ..., T1);

void h(int x, float& y) {
  const int z = x;
  g1(x, y, z);                  // 错误:Types无法被推导
}

这个例子确实编译失败,符合标准描述里的“无法推导”的结论。但如果我把调用改成只传一个参数,情况就不一样了:

template<class T1, class ... Types> void g1(Types ..., T1);

void h(int x, float& y) {
  const int z = x;
  g1(x); // 编译通过
}

这个版本居然能正常编译,这就让我产生了疑问:

标准里有没有明确规定这种情况下参数包应该被视为空包?为什么第二个例子不会触发编译错误?

我理解第二个例子里,Types只能是空包才能让函数签名匹配,但第一个例子里“x, y”对应Types看起来逻辑上也完全成立啊。我知道第一个例子里Types处于非推导上下文所以无法被推导,所以编译失败,但第二个例子里Types同样处于非推导上下文,按照第一个例子的错误逻辑,这个例子也应该是非法的才对。

甚至我忍不住想,开头引用的标准描述是不是应该改成:"当函数参数包出现在非推导上下文时,该包被推导为空包。"(当然这么说有点自相矛盾,毕竟非推导上下文里的东西本不该被推导,但这不正是实际编译器的行为吗?)

另外,之前看到有评论说“它不是被推导为空,而是属于非推导情况,使用调用时提供的实参(也就是空)”,一开始我觉得这好像是合理的解释,但再看下面这个例子:

#include <type_traits>

template <typename A, typename ...B, typename C>
void foo(A, B..., C) {
    static_assert(std::is_same_v<A, int>);
    static_assert(sizeof...(B)==0);
    static_assert(std::is_same_v<C, const char *>);
}

int main() {
    foo(42, "");
}

这里我就有点纠结了:调用foo的时候根本没有指定任何模板实参,那所谓“调用时提供的实参”从何而来?如果是foo<int>(42, "")这种写法,我还能说服自己<int>相当于<int, 空包>,但这个例子里连模板实参列表都没有,总不能说“空的模板实参列表默认给参数包传了空实参”吧?这种逻辑实在有点反直觉。

备注:内容来源于stack exchange,提问作者geza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:23:02