3

考虑下面的代码:

template<char>
struct S { }; 

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return S<ref[0]>{};
}

int main() {
    constexpr auto v = f("foo");
    (void)v;
}

它不能编译为ref[0]不是一个常量表达式。

无论如何,下面的代码编译得很好:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return ref[0];
}

int main() {
    constexpr auto v = f("foo");
    (void)v;
}

他们是否应该出于或多或少相同的原因编译或失败?

[expr.const]我们得到:

条件表达式e核心常量表达式,除非e[...] 的评估将评估以下表达式之一:
[...]
- 一个 id 表达式,它引用引用类型的变量或数据成员,除非引用有一个前面的初始化并且要么
    - 它用一个常量表达式初始化,要么 - 它
    的生命周期开始于e;

无论如何,在这种情况下,它是用常量表达式初始化的,并且生命周期与 相同e,因此该规则不适用。

我的推理有什么问题?

作为一个附带问题,我会问是否可以使用这样的数组或其中的一部分作为模板参数。

4

2 回答 2

2

这:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return S<ref[0]>{};
}

格式不正确,因为根据 [temp.arg.nontype]:

非类型模板参数的模板参数是模板参数类型的转换常量表达式(5.20

并来自 [expr.const]:

条件表达式e 是核心常量表达式,除非按照抽象机 (1.9) 的规则对 e的求值将求值以下表达式之一: [...]
(2.7) — 左值到右值转换 (4.1) 除非它应用于
(2.7.1) — 整数或枚举类型的非易失性左值,它引用具有先前初始化、使用常量表达式初始化的完整非易失性 const 对象,或
(2.7. 2) — 一个非易失性泛左值,它指代字符串文字 (2.13.5) 的子对象,或
(2.7.3) — 一个非易失性泛左值,它指代一个用 constexpr 定义的非易失性对象,或者指到此类对象的非可变子对象,或
(2.7.4) — 文字类型的非易失性左值,指的是一个非易失性对象,其生命周期开始于对 e 的评估;

ref[0]需要左值到右值的转换,并且这些子项目符号都不适用。请注意,这ref不是字符串文字,因此 2.7.2 不适用,也不是用 定义的constexpr,因为它是函数参数,我们没有这种能力。

我们基本上需要将字符串文字作为文字传递的能力,这还不存在。


另一个例子:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return ref[0];
}

不需要转换后的常量表达式——它是由模板非类型参数引入的。constexpr这段代码很好,只有当您尝试使用非constexpr数组值初始化变量时才会出现问题。

于 2016-10-17T14:28:27.030 回答
0

第一个示例不应该编译,因为您不能拥有仅编译时的 constexpr 函数(或编译时重载,如 D's __cfte)。

按照这个推理,如果您f在运行时调用第一个示例,它的返回类型是什么?

至于附带的问题:Boost Hana 尽管仅支持最新标准,但仅将字符串文字用于运行时内容,因此可能是不可能的。

于 2016-10-17T14:27:31.390 回答