理解《C++ Templates完全指南》中decltype与返回类型的脚注注释
核心原理
decltype单独作用于调用表达式的规则:
C++标准明确规定,当decltype的操作数是单个调用表达式时,仅验证调用本身的语法合法性,不需要返回类型满足「非引用/非void则必须为完整类型」的要求。哪怕返回的是仅做了前置声明、没有完整定义的类类型,只要调用语法合法,decltype就能正常推导出对应的类型,不会触发编译错误。- 逗号表达式的隐含要求:
当你写成decltype(std::declval<T>().begin(), 0)时,decltype的操作数变成了逗号表达式。逗号表达式的求值规则是先求值左操作数、丢弃其结果,最终返回右操作数的类型。而被丢弃的值如果是类类型,C++要求该类型必须是完整类型——因为编译器需要确认是否要调用析构函数,以及析构函数是否可访问。
你的测试用例没有触发差异的原因
你测试用例里的A::begin返回的是void类型,void不属于类类型,不会触发「丢弃值表达式要求类型完整」的检查,所以两种写法下HasBeginT<A>都能匹配到偏特化版本,断言自然都能通过。
可复现差异的测试用例
你可以用返回不完整类型的begin测试,就能看到明显区别:
// 仅前置声明,没有定义,属于不完整类型 struct Incomplete; struct C { Incomplete begin(); }; // 原版写法:可以正常编译,匹配到true_type的偏特化 static_assert(HasBeginT<C>::value, ""); // 如果把HasBeginT的偏特化改成decltype((std::declval<T>().begin(), 0)) // 这行static_assert会编译失败,因为Incomplete是不完整类型,无法作为丢弃值表达式存在
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

