考虑以下
struct dummy{};
dummy d1;
dummy d2;
template<dummy* dum>
void foo()
{
if (dum == &d1)
; // do something
else if (dum == &d2)
; // do something else
}
现在,可以像这样调用foo
foo<&d1>();
foo<&d2>();
一切都按预期工作。但是以下不是
constexpr dummy* dum_ptr = &d1;
foo<dum_ptr>();
Visual Studio出现此错误
错误C2975:
dum_ptr
:foo
的模板参数无效,预期编译时常量表达式
虽然这有效
constexpr dummy& dum_ref = d1;
foo<&dum_ptr>();
在visual studio中,但不是在G ++中,因为
注意:模板参数扣除/替换失败:
错误:& dum_ref
不是dummy*
的有效模板参数,因为它不是变量的地址foo<&dum_ref>();
编辑:
从C ++ 17开始,std::addressof
被标记为constexpr,所以我猜它应该有效。
答案 0 :(得分:4)
海湾合作委员会是对的。
表达式绝对是constant-expression
s *,因为它们被分配给constexpr
变量。但是,在c ++ 14之前,对指针模板参数允许的内容还有其他限制。
C ++ 14草案N4140 [temp.arg.nontype]
1非类型非模板模板参数的模板参数应为以下之一:
- 表示整数或枚举类型的非类型模板参数,转换后的常量表达式(5.19)的类型 模板参数;或
- 非类型模板参数的名称;或
- 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部的完整对象的地址 连接或具有外部或内部联系的功能,包括 函数模板和函数template-id但不包括非静态 类成员,表达(忽略括号)为
&id-expression
,其中id-expression是对象的名称或 功能,除了&amp;如果名称引用a,则可以省略 函数或数组,如果相应则应省略 template-parameter是一个引用;或- 一个求值为空指针值的常量表达式(4.10);要么 一个常量表达式,其值为null成员指针值(4.11);要么 指向成员的指针,如5.3.1所述;要么 类型为std :: nullptr_t的常量表达式。
对于foo<dum_ptr>()
,dum_ptr
不表示为&name
,而对于foo<&dum_ref>()
,dum_ref
不是对象的名称,它是对象的引用名称,因此两者都不允许作为模板参数。
这些限制在c ++ 17中解除,以允许任何constexpr,这就是为什么它在那里工作:
C ++ 17 draft N4606 - 14.3.2模板非类型参数[temp.arg.nontype]
1非类型模板参数的模板参数应为a 转换常数表达式(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
*(参见地址常量表达式和参考常量表达式)