将本地字符串参数作为const的引用传递到constexpr函数中?为什么这是合法的

Passing local string parameter as reference to const into a constexpr function? Why is that legal?

本文关键字:函数 constexpr 为什么 参数 字符串 引用 const      更新时间:2023-10-16

constexpr函数只接受文字类型的参数。类字符串的对象本身不是文字类型,而是指针(是标量类型(或引用是文字类型。因此,对字符串的引用是一种文字类型。

此外,如果constexpr函数的参数也是常量表达式,则该函数仅返回一个常量表达式。

"如果scale*函数的参数是一个常量表达式,但不是其他表达式:">
来源:C++初级读本,第5版

刻度在这种情况下是is_shorter((

现在,我将两个对字符串的引用传递给constexpr函数,该函数不保留在固定地址(全局或静态(,而是保留在可变地址(本地(。因此,两个参数都不应该是常量表达式
constexpr函数的结果被分配给constexpr布尔

#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
/*
 * it isn't possible to define is_shorter as constexpr function
 * 1. string is not a literal type, but can be passed as pointer or reference,
 *    which are literal types.
 * 2. the member function size() is not a constexpr function
 *
 */
// compare the length of two strings
constexpr bool is_shorter(const string& s1, const string& s2) {
    // compiler message from gcc: 'error: call to non-constexpr function'
    // return s1.size() < s2.size();
    // compare addresses of pointers, which is legal or illegal but useless, because
    // both parameters are local objects and not stored at fixed addresses.
    return &s1 != &s2;
}
int main() {
    string shrt = "short";
    string longer = "longer";
    cout << "address of shrt " << &shrt << "n";
    cout << "address of longer " << &longer << "n";
    constexpr bool state = is_shorter(shrt, longer);
    if (state) {
        cout << "shrt is shortern";
    } else {
        cout << "longer is longern";
    }
    cout << "string " << is_literal_type<string>::value << "n";
    cout << "string& " << is_literal_type<string&>::value << "n";
    cout << "string* " << is_literal_type<string*>::value << "n";
    return 0;
}   

编译:

$ g++ -o ex646 ex646.cpp -std=gnu++11 -Wall -Wpedantic

运行:

$ ./ex646
address of shrt 0x7ffd39e41a50 # every time different
address of longer 0x7ffd39e41a40 # every time different, always 0x10 more than &shrt
shrt is shorter
string 0
string& 1
string* 1

编译器如何能够比较两个字符串的地址?它们在程序的每个运行时都是不同的,而它们的相对位置保持不变。这种用法的关键是,至少它们彼此的相对位置保持不变吗?

更新2015-08-12
在撰写本文时,这看起来像是ISO标准的"恒定评估规则中的一个错误"(如果我错了,请纠正我(。请参阅isocpp.org列表中的此讨论。因此,这不应该是GCC和CLANG内部的编译器错误。

谢谢dyp!

据我所知,这看起来像是某种编译器扩展或bug。C++11通过缺陷报告进行了调整,允许将引用视为文字类型,无论其引用的变量是否为文字类型。但是,引用常量表达式应该引用静态存储持续时间的对象,地址常量表达式也应该类似。

C++11标准草案告诉我们在哪些情况下指针可以被认为是相等的:

如果并且只有当它们都为空,都指向相同的函数,或者都表示相同的地址时(3.9.2(

因此,在您的情况下,编译器可以简单地推断出!=属于哪种情况,但我看不出它会避免这种情况下的常量表达式要求。

参考章节5.19[expr.const]请告诉我们:

引用引用类型的变量或数据成员的id表达式,除非该引用具有之前的初始化,使用常量表达式初始化

缺陷报告1454对此进行了修改,修改如下:

引用引用类型的变量或数据成员的id表达式,除非该引用具有之前的初始化和

  • 它是用常量表达式初始化的,或者
  • 它是一个对象的非静态数据成员,其生存期在e的求值范围内开始

但在任何一种情况下,这都应该足以使这种情况不是一个常数表达式。

引用常量表达式是左值核心常量表达式,用于指定具有静态存储持续时间或函数的对象。地址常量表达式是指针类型的prvalue核心常量表达式,其计算结果为具有静态存储持续时间的对象、函数地址、空指针值或prvalue核心std::nullptr_t类型的常量表达式。

这些都不适用于您案例中的自动变量。

参考文献最初不是文字,而是缺陷报告1195,其中写道:

7.1.5[dcl.constexpr]第3段要求constexpr函数或构造函数的引用参数和返回类型必须引用文字类型,这一要求过于严格。5.20[expr.const]第2段已经防止了对非文字类型的lvalue的任何有问题的使用,并且它允许使用指向非文字类型作为地址常量的指针。通过引用参数和constexpr函数的返回类型也应该允许这样做。

并更改CCD_ 3[基本类型]部分,该部分在CCD_

如果类型是:,则该类型为文字类型

[…]

  • 引用类型,所述引用类型指代文字类型;或

至:

  • 参考类型;或

字符串的地址是堆栈对象的地址,而不是字符串正在包装的底层指针。每次运行的地址可能不同,但编译器每次都会以相同的方式将变量加载到内存中,因此比较总是会产生相同的结果。

注意:不同的编译器可能会以不同的方式对变量进行排序,因此此代码不可移植。

注2:比较相等的指针(==,!=(是为相同类型的指针定义的行为。尝试与关系运算符(<,>,<=,>=(进行比较是未指定的if:

相同类型的两个指针p和q指向不同的对象,这些对象不是同一对象或同一数组的元素的成员,或者指向不同的函数,或者如果其中只有一个是空