问题描述
请考虑以下模板:
using IntFnPtr = int(*)(int);
template <IntFnPtr> void f() { }
这些测试:
int g(int) { }
int main()
{
f<&g>(); // OK
const IntFnPtr cp = &g;
f<cp>(); // Error -- why?
constexpr IntFnPtr cexprp = &g;
f<cexprp>(); // OK
}
为什么尝试用格式错误的f
实例化cp
?编译器抱怨:
> error: no matching function for call to 'f'
请注意,这似乎与其他实体不一致,例如整数:
template <int> void f() { }
int main()
{
f<5>(); // OK
const int ci = 5;
f<ci>(); // OK
constexpr int cexpri = 5;
f<cexpri>(); // OK
}
解决方法
首先,有temp.arg.nontype#2:
非类型模板参数的模板参数应为模板参数类型的转换常量表达式([expr.const])。
[注1:如果模板参数是一个重载集合(或重载集合的地址,包括形成一个指向成员的指针),则从该集合中选择匹配函数([over.over])。 —尾注]
然后我们可以将其跟随到expr.const#10:
类型T的转换后的常量表达式是隐式转换为类型T的表达式,其中转换后的表达式是常量表达式,并且隐式转换序列仅包含...
根据该规则,我们可以看到变量cp
可能不是常量表达式,因为根据expr.const#3,它甚至不是潜在恒定的: / p>
如果变量是constexpr或具有引用或const限定的整数或枚举类型,则它可能是常量。
因此cp
对于非类型模板参数不是有效的模板参数,并且会出现错误。
请注意,这就是ci
出现明显不一致之处。由于ci
具有const限定的整数类型,因此可以用作非类型模板参数的模板参数。
类似地,也允许对f
的所有其他调用,因为每种情况下的模板参数都是一个常量表达式:
-
g
是具有外部链接的函数,因此其地址是一个常量表达式。 -
5
是一个常数整数表达式。 -
cexprp
和cexpri
都是constexpr
变量。