C++ using声明是否在所有场景下都允许使用不完整类型?
C++ using声明语义与示例代码的合法性判定
using别名声明的核心语义
using 别名名 = 类型说明符;是类型别名声明,不会定义新类型,仅为指定的类型(或类模板特化)绑定一个可替换的别名,语义和传统的typedef完全一致,仅额外支持模板别名的写法。- 别名声明本身不会触发类模板特化的隐式实例化,写别名的阶段不需要别名指向的类型是完整类型。
类模板隐式实例化的触发规则
- C++标准明确规定:只有当上下文需要类模板特化的完整定义时,编译器才会隐式实例化该特化。需要完整定义的典型场景包括:计算该类型的
sizeof/alignof、定义该类型的实例、访问该类型的成员、将该类型作为函数参数按值传递等。 - 仅在代码中提及类模板特化的名字、为其声明别名、用该类型定义指针/引用、作为函数形参类型(非按值传递场景,比如
std::initializer_list<T>本身只持有T的指针,不需要T完整),都属于不需要完整类型的上下文,不会触发模板实例化。
示例代码的具体分析
#include <variant> #include <iostream> struct box; using val = std::variant<std::monostate, box, int, char>; struct box { int a; long b; double c; box(std::initializer_list<val>) { } }; int main() { std::cout << sizeof(val) << std::endl; }
- 代码中
using val = ...的位置仅做别名声明,不会触发std::variant的实例化,此时box是不完整类型完全合法,不存在违规。 box构造函数的形参是std::initializer_list<val>,该类型本身内部只持有val类型的指针,不需要val是完整类型,因此这一行也不会触发std::variant的实例化。- 直到
main函数中执行sizeof(val)时,才需要val对应的std::variant特化的完整定义,此时box已经完成了完整定义,实例化过程完全符合标准要求。 - 额外说明:C++17及之后的标准库规范明确要求
std::variant支持不完整类型作为模板参数,只要触发实例化的点位上所有模板参数均为完整类型即可,因此该代码完全合法,不存在未定义行为,GCC、Clang下输出32是标准规定内的正确表现。 - 如果把
sizeof(val)的代码移到struct box的完整定义之前,才会触发未定义行为——因为此时实例化std::variant用到了不完整的box类型,违反标准约束。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

