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

为何C++20允许将模板用作非类型模板参数?

C++20中类模板实例作为非类型模板参数的合法性解析

我发现自C++20起,将模板实例用作非类型模板参数的“类型”是被允许的,示例代码如下:

template< typename T >
struct LiteralType {
    T a, b, c;
};

template< LiteralType t >
struct S {
    static constexpr auto value = t;
};

auto f() {
    return S< LiteralType< int >{} >::value;
}

我的疑问是:

  • 为什么这种写法是被允许的?
  • 直观上LiteralType的每个实例化都是字面类型(或结构类型),但标准中哪里有正式规定?
  • 粗略查阅标准草案[temp.param]p6部分后,发现里面说仅允许“类型”作为非类型模板参数,而非模板,这似乎和示例矛盾?

核心解释

你这里的写法并不是把模板作为非类型模板参数的类型,而是利用了C++20引入的类模板实参推导(CTAD),用类模板名作为占位符,让编译器推导出具体的实例化类型(也就是LiteralType<int>这种具体类类型),最终作为非类型模板参数的类型。

一、合法性的来源

在template< LiteralType t >的声明中,LiteralType并非直接作为模板使用,而是触发了CTAD:编译器会根据传入的模板实参LiteralType<int>{},推导出类模板LiteralType的模板实参为int,从而确定非类型模板参数的实际类型是LiteralType<int>。

这是C++20对非类型模板参数的扩展规则——允许在非类型模板参数的类型位置使用类模板名,只要能通过后续的模板实参推导出具体的实例化类型。

二、标准中的正式依据

  1. 非类型模板参数的类型要求
    C++20标准在[temp.param]p4中明确,非类型模板参数的类型必须是字面类型(literal type)。而LiteralType<int>完全符合字面类型的定义:

    • 它是聚合类型(aggregate type),且所有成员(int类型的a/b/c)都是字面类型;
    • 根据[basic.types.general]p10,聚合类型属于字面类型的范畴。
  2. 类模板名作为非类型模板参数类型的规则
    针对你提到的[temp.param]p6,该条款的完整逻辑是:非类型模板参数的类型必须是一个合法的“类型”,但允许用类模板名作为类型占位符,前提是编译器能通过模板实参推导出具体的实例化类型。这里的“类型”包括推导得到的具体实例化类型,并非禁止使用类模板名来声明参数。

三、关键总结

  • 代码中的非类型模板参数实际类型是LiteralType<int>,属于合法的字面类型,满足C++20对非类型模板参数的类型要求;
  • template< LiteralType t >中的LiteralType是CTAD的占位符,最终会被推导为具体类型,而非直接将模板作为类型;
  • 字面类型的定义确保了LiteralType<T>的实例化类型可以在编译期构造、复制,符合非类型模板参数的编译期常量要求。

内容的提问来源于stack exchange,提问作者mkluwe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 22:35:18