以下代码使用gcc和MSVC进行编译,但是使用clang我测试的clang-3.5和当前主干无法使用。
template <typename T>
constexpr auto wrong = false;
template <typename T>
constexpr auto foo(const T t) -> int
{
static_assert(wrong<T>, "");
return {};
}
using F = decltype(foo(1));
int main() {}
clang实例化函数体并在static_assert
上绊倒。 gcc和MSVC只是查看函数声明并忽略正文中的static_assert
。
如果删除constexpr,所有编译器都可以编译代码。
问题:
如果声明了返回类型,是否允许decltype查看函数体?
我正在寻找标准中相应部分的参考。
答案 0 :(得分:4)
历史记录:正如评论中所述,此问题已提出为CWG issue 1581。在isocpp.org thread中,Columbo认为代码是有效的,因为模板永远不会在未评估的操作数中实例化,但是on a clang bug report,&#34; rsmith&#34;反驳说,某些decltype
表达式确实需要模板实例化。
clang线程通过为decltype
实例化constexpr
模板提出自己的(非标准)标准,暂时解决了这个问题。从4.0版开始,clang确实成功编译了代码。
WG21的Richard Smith已于2017年11月开始使用P0859解决此问题。这会向[expr.const]添加新文本,实现clang的行为,如上所述:
表达式可能是常量评估如果是:
- 潜在评估的表达式([basic.def.odr]),
- 一个约束表达式,包括一个由 requires-clause 的 constraint-logical-or-expression 组成的,
- braced-init-list的立即子表达式 [脚注:可能需要进行常量评估以确定是否执行缩小转换([dcl.init.list] ])。 ],
- 在模板化实体中出现的
&
cast-expression 形式的表达式[脚注:可能需要进行常量评估以确定是否有这样的表达式是依赖于价值的([temp.dep.constexpr])。 ],或- 上述其中一个的子表达式,它不是嵌套的未评估操作数的子表达式。
如果是
,则需要进行常量评估。
- 由表达式([basic.def.odr])命名的constexpr函数,该函数可能被持续评估,或者
- 一个变量,其名称显示为潜在的常量计算表达式,该表达式是constexpr变量或者是非易失性const限定的整数类型或引用类型。
并修改[temp.inst]以表示如果模板专门化的定义影响程序的语义,则实例化,这意味着它的需要用于常量评估,如上所述,即使实际上并不需要,可以这么说。
修改ODR以避免Columbo的反对意见。
该提议的建议更改确实出现在N4727中,这是一个后C ++ 17草案。所以我认为即使P0859链接和CWG缺陷列表还没有这样说,它们已被接受。
根据这些更改,您的代码decltype(foo(1))
,表达式foo(1)
不是可能会持续评估(因为它与上面的任何项目符号都不匹配),所以模板不能实例化,并且代码经过修改以避免[dcl.constexpr] / 6,应该成功编译。
(C ++ 17 dcl.constexpr / 6表示如果没有有效的专业化,模板是格式错误的NDR; foo
也是如此,但这可以通过添加{{1}来解决例如)。