Zer*_*ges 14 language-lawyer c++11
考虑以下
struct dummy{};
dummy d1;
dummy d2;
template<dummy* dum>
void foo()
{
if (dum == &d1)
; // do something
else if (dum == &d2)
; // do something else
}
Run Code Online (Sandbox Code Playgroud)
现在,可以foo像这样打电话
foo<&d1>();
foo<&d2>();
Run Code Online (Sandbox Code Playgroud)
一切都按预期工作.但是以下没有
constexpr dummy* dum_ptr = &d1;
foo<dum_ptr>();
Run Code Online (Sandbox Code Playgroud)
来自Visual Studio的这个错误
错误C2975 ::
dum_ptr无效的模板参数foo,预期的编译时常量表达式
虽然这有效
constexpr dummy& dum_ref = d1;
foo<&dum_ptr>();
Run Code Online (Sandbox Code Playgroud)
在visual studio中,但不是在G ++中,因为
注意:模板参数推断/替换失败:
错误:& dum_ref不是有效的模板参数,dummy*因为它不是变量的地址Run Code Online (Sandbox Code Playgroud)foo<&dum_ref>();
编辑:
自从C++ 17,std::addressof被标记为constexpr,所以我猜它应该工作.
GCC 在这一点上是正确的。
这些表达式肯定是constant-expressions*,因为它们被分配给一个constexpr变量。但是,在 c++14 之前,对于指针模板参数允许的内容还有其他限制。
C++14 草案 N4140 [temp.arg.nontype]
1 非类型、非模板模板参数的模板参数应为以下之一:
- 对于整型或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或者
- 非类型模板参数的名称;或者
- 常量表达式(5.19),指定具有静态存储持续时间和外部或内部链接的完整对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板ID,但不包括非静态类成员,表示为(忽略括号)as
&id-expression,其中 id 表达式是对象或函数的名称,但如果名称引用函数或数组,则可以省略 &;如果相应的模板参数是引用,则应省略 &;或者- 计算结果为空指针值的常量表达式 (4.10);或计算结果为空成员指针值的常量表达式 (4.11);或指向成员的指针,如 5.3.1 中所述表示;或 std::nullptr_t 类型的常量表达式。
For foo<dum_ptr>(),dum_ptr不表示为&name,并且 for foo<&dum_ref>(),dum_ref不是对象的名称,而是对该对象的引用的名称,因此两者都不允许作为模板参数。
这些限制在 c++17 中被取消以允许任何 constexpr,这就是它在那里工作的原因:
C++17 草案 N4606 - 14.3.2 模板非类型参数 [temp.arg.nontype]
1 非类型模板参数的模板参数应为模板参数类型的转换常量表达式(5.20)。对于引用或指针类型的非类型模板参数,常量表达式的值不应引用(或对于指针类型,不应是以下地址):
- (1.1) 子对象 (1.8),
- (1.2) 临时对象 (12.2),
- (1.3) 字符串文字 (2.13.5),
- (1.4) typeid 表达式 (5.2.8) 的结果,或者
- (1.5) 预定义的 __func__ 变量 (8.4.1)。
像往常一样,clang 给出了最好的错误消息: https://godbolt.org/g/j0Q2bV
*(参见地址常量表达式和引用常量表达式)