将本地字符串参数作为const的引用传递到constexpr函数中?为什么这是合法的
Passing local string parameter as reference to const into a constexpr function? Why is that legal?
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指向不同的对象,这些对象不是同一对象或同一数组的元素的成员,或者指向不同的函数,或者如果其中只有一个是空
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- constexpr构造函数需要常量成员函数时出现问题
- constexpr 函数获取常量字符*
- 如何在 constexpr 函数中实现回退运行时
- 为什么我不能在 constexpr lambda 函数中使用 std::tuple
- 在非 constexpr 函数中作为左值传递的变量上使用 'constexpr' 函数
- 在 constexpr 构造函数 (c++17) 中赋值到 const char * 在使用 Android NDK 时
- NVCC 错误:string_view.h:constexpr 函数返回是非常量
- constexpr函数中的静态constexpr变量
- 在 constexpr funnction 中调用basic_string函数
- 为什么我的 constexpr 对象在我的函数中不是 constexpr?
- constexpr log10 整数函数
- C++:初始化 constexpr 构造函数中的成员数组
- 编译器生成的默认构造函数具有 constexpr 混淆行为
- 在 constexpr 构造函数中初始化数组是否合法?
- 在 constexpr 函数中断言
- MSVC 和函数参数的 constexpr?
- 具有位域的结构的 Constexpr 构造函数
- C++ 默认构造函数 constexpr 或正确未定义