我有三个函数调用,我认为应该对它们进行相同的处理,但显然它们不是.我试图理解为什么三个中的一个不编译(g ++ -std = c ++ 0x).
// Minimal example to reproduce a compile bug I want to understand.
#include <iostream>
#include <string>
using namespace std;
void bar(const string &&x) { cout << "bar: " << x << endl; }
string returns_a_string() { return string("cow"); }
int main( int argc, char *argv[] )
{
bar(string("horse")); // ok
bar(returns_a_string()); // ok
string aardvark = "aardvark";
bar(aardvark); // not ok, fails to compile, error in next comment
/*
rvalue-min.cpp:29:22: error: cannot bind …Run Code Online (Sandbox Code Playgroud) 在C++ 14标准(n3797)中,左值转换的左值部分如下所示(强调我的):
4.1左值 - 右值 - 转换[conv.lval]
T可以将非函数非数组类型的glvalue(3.10)转换为prvalue.如果T是不完整类型,则需要进行此转换的程序格式不正确.如果T是非类类型,则prvalue的类型是cv-nonqualified versionT.否则prvalue的类型是T.当在未评估的操作数或其子表达式中发生左值到右值转换时(第5条),不访问引用对象中包含的值.在所有其他情况下,转换结果根据以下规则确定:
- 如果
T是(可能是cv限定的)std::nullptr_t则结果是空指针常量.- 否则,如果
T有类类型,则转换复制 -T从glvalue 初始化一个临时类型,转换的结果是临时的prvalue.- 否则,如果glvalue引用的对象包含无效指针值,则行为是实现定义的.
- 否则,如果
T是(可能是cv限定的)无符号字符类型,并且glvalue引用的对象包含不确定的值,并且该对象没有自动存储持续时间,或者glvalue是一元运算&符的操作数,或者它是绑定到引用,结果是一个未指定的值.- 否则,如果glvalue引用的对象具有不确定的值,则行为未定义.
- 否则,glvalue指示的对象是prvalue结果.
- [ 注:另见3.10]
这一段有什么意义(粗体)?
如果此段落不在此处,那么它适用的情况将导致未定义的行为.通常,我希望unsigned char在具有不确定值时访问值会导致未定义的行为.但是,这一段意味着
&或将其绑定到引用,或者unsigned char没有自动存储时间,然后转换产生一个未指定的值,而不是未定义的行为.
我是否正确地得出这个程序的结论:
#include <new>
#include <iostream>
// using T = int;
using T = unsigned char;
int main() {
T * array = new T[500];
for …Run Code Online (Sandbox Code Playgroud) c++ undefined-behavior language-lawyer lvalue-to-rvalue c++14
我正在编写遵循此标准的C编译器,如果我解析这样的语句:
int i;
(i) = 1;
Run Code Online (Sandbox Code Playgroud)
我的编译器将报告一个错误,指出该错误(i)是一个右值,不应分配。
我检查了代码和规则,并在赋值表达式语义中发现了这一点:
赋值运算符的左值应为可修改的左值。
赋值表达式具有赋值后的左操作数的值,但不是左值。
在我的情况下,有两个赋值表达式:
(i) = 1和i括号中。因此,(i)应该是一个右值。
所以我的问题是:(i) = 1在此C标准中是否
非法?
c variable-assignment rvalue language-lawyer lvalue-to-rvalue
class Test {
public:
int n1;
};
Test func() {
return Test();
}
int main() {
func() = Test();
}
Run Code Online (Sandbox Code Playgroud)
这对我来说没有意义.如何以及为什么允许这样做?是不确定的行为?如果函数返回一个rvalue,那么如何将rvalue设置为另一个rvalue呢?如果我尝试使用任何原始类型,它会给我一个像我期望的错误.
我知道左值是内存中的一个位置,创建临时左值(rvalue?)并将其赋值给另一个左值的函数也是如此?有人可以解释这种语法的工作原理吗
实际上,我在网上看到的每个左值到右值转换的例子都与诸如int等的基本类型有关。
我自己找不到适用于类类型的 l2r 示例;在所有看似适用的例子中,通常有一个函数涉及 lvalue-ref(如 copy-ctor),为此 l2r 似乎被抑制(参见例如这个问题)。
然而,在 l2r 本身的描述中,有一个关于类类型的子句(来自 [conv.lval]):
转换的结果根据以下规则确定:
<...> 如果 T 具有类类型,则转换从泛左值复制初始化 T 类型的临时值,并且转换的结果是临时值的纯右值。
有人可以举一个这个条款的例子吗?我不能。
当使用不同的右值引用类型调用重载函数时,我在代码中遇到了意外行为。下面的代码片段演示了这个问题:
#include <iostream>
void foo(int&& x)
{
std::cout << "int Rvalue: " << x << std::endl;
}
void foo(float&& x)
{
std::cout << "float Rvalue: " << x << std::endl;
}
int main()
{
int int_x = 1;
float float_x = 5.14f;
foo(int_x);
foo(float_x);
}
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)float Rvalue: 1 int Rvalue: 5
我没有得到预期的输出(应该是“int Rvalue:1”和“float Rvalue:5”),而是观察到相反的顺序。我发现这种行为令人困惑,并且希望了解为什么会发生这种情况。
我确保理解右值引用的概念以及基于它们的重载如何工作。我也尝试过搜索类似的问题,但没有找到任何直接解决此特定情况的问题。
任何指导或解释将不胜感激。预先感谢您的帮助!
C++ ANSI ISO IEC 14882 2003附录C.1(第668页):
更改:条件表达式,赋值表达式或逗号表达式的结果可能是bean左值基本
原理:C++是面向对象的语言,相对更重视左值.例如,函数可以返回左值.
对原始特征的影响:改变定义明确的特征的语义.隐式依赖左值到右值转换的某些C表达式将产生不同的结果.例如,
char arr[100];
sizeof(0, arr)
Run Code Online (Sandbox Code Playgroud)
在C + +,并产生100 sizeof(char*)中C.
...
我今天正在读这篇文章,我记得几个月后,我的一个朋友提出了一个问题,即编写一个函数,如果用C++编译将返回0,如果用C编译则返回1.我解决了它利用了这样一个事实,即在一个结构中,结构是在外部范围内.因此,考虑到这些新信息,我认为这将是上述问题的另一种解决方案,我在Microsoft Visual Studio 2008上尝试过,但无论是否编译为C或C++代码sizeof(0, arr)总是产生4.所以2个问题:
1.什么是ISO C?它是目前的C标准吗?它是唯一的一个(我听说C正在迅速发展)2.这是一个微软的C++漏洞吗?
TIA
编辑:抱歉与输出混淆并编辑它:
我一直在互联网上阅读很多,似乎很多人提到了以下规则(但我在标准中找不到它),
加法运算符+(和所有其他二元运算符)要求两个操作数都是rvalue,结果是rvalue.等等..
我检查了C++标准,并明确指出(第3.10/2条),
每当glvalue出现在期望prvalue的上下文中时,glvalue就会转换为prvalue
(第5/9条),
每当glvalue表达式作为操作符的操作数出现时,该操作符需要该操作数的prvalue,左值到右值(4.1),数组到指针(4.2)或函数到指针(4.3)标准转换是用于将表达式转换为prvalue.
它使用一个术语操作数"期望"一个prvalue.但是,当我查看加法运算符,乘法运算符等时,它只提到,结果是一个prvalue,但它没有说明操作数是"预期"的内容.
二元运算符是否真的期望操作数是prvalue在以下情况下会有所不同,
int b = 2;
int a = b + 1;
Run Code Online (Sandbox Code Playgroud)
如果b预期是prvalue,那么这里将进行左值到右值的转换,然后它将执行prvalue + prvalue并返回一个prvalue,结果prvalue被赋值给一个左值a.
但是,如果b不需要是prvalue,则它将是lvalue + prvalue,结果是prvalue.
我真的想知道标准在哪里明确地或隐含地提到不同运营商的规则?我检查了所有运算符部分和只有少数运算符,标准明确提到操作数和结果是左值还是右值.对于大多数运营商而言,标准仅提及结果,而不是操作数要求.
谢谢.
顺便说一下,我在标准5.19中发现关于常量表达式可能非常"隐含地"暗示二元运算符需要对操作数进行左值到右值的转换.有关详细信息,请参阅我之前的问题,
条件表达式是常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2).
...
- 除非适用,否则为左值 - 右值转换(4.1)
----一个整数或枚举类型的glvalue,它引用一个带有前面初始化的非易失性const对象,用一个常量表达式初始化
谢谢阅读.
编辑:考虑以下两个例子:
std::string x;
{
std::string y = "extremely long text ...";
...
x = y; // *** (1)
}
do_something_with(x);
Run Code Online (Sandbox Code Playgroud)
struct Y
{
Y();
Y(const Y&);
Y(Y&&);
... // many "heavy" members
};
struct X
{
X(Y y) : y_(std::move(y)) { }
Y y_;
}
X foo()
{
Y y;
...
return y; // *** (2)
}
Run Code Online (Sandbox Code Playgroud)
y在线(1)和(2)的两个例子中,它的寿命接近结束并且即将被破坏.很明显,它可以被视为右值并在两种情况下都可以移动.在(1)中,它的内容可以移入x和移入(2)到temp的实例中X().y_.
我的问题是:
1)是否会在上述任何一个示例中移动?(a)如果是,则采用何种标准规定.(b)如果不是,为什么不呢?这是标准中的遗漏还是我没有想到的另一个原因?
2)如果上述答案为否.在第一个例子中,我可以改变(1)x = std::move(y)以强制编译器执行移动.在第二个示例中,我可以做什么来向编译器指示y可以移动?return std::move(y)?
注意:我故意返回(2)中的实例Y而不是X …
是否需要C实现来忽略无效表达式评估期间发生的未定义行为,就好像评估本身从未发生过一样?
考虑C11,6.3.2.2§1:
如果将任何其他类型的表达式评估为void表达式,则将其值或指示符丢弃。(将评估void表达式的副作用。)
这与用于防止编译器警告未使用的变量的常用用法有关:
void f() {
int a;
(void)a;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们有未定义的行为,例如:
void f() {
int a;
(void)(1/0);
}
Run Code Online (Sandbox Code Playgroud)
我可以安全地声明该程序不包含未定义的行为吗?该标准说“它的值或指示符被丢弃”,但是“表达式(...)被评估(...)”,因此评估似乎确实发生了。
GCC / Clang确实报告了未定义的行为,因为在这种情况下很明显,但是在一个更微妙的示例中,它们没有:
int main() {
int a = 1;
int b = 0;
(void)(a/b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
即使这样-O0,GCC和Clang都无法评估1/0。但这即使没有强制转换也会发生,因此它不具有代表性。
将论点推到极致,(void)a在我的第一个示例(a未初始化的地方)中的简单评估是否会系统地触发未定义的行为?
ISO C11 6.3.2.1§2确实提到:
如果左值指定了可以使用寄存器存储类声明的自动存储持续时间的对象(从未使用其地址),并且该对象未初始化(未使用初始化器声明,并且在使用前未对其进行任何赋值) ),则行为未定义。
但是,在附件J.2未定义行为中,措词略有不同:
在以下情况下,行为是不确定的:
(...)
在需要指定对象值的上下文中使用了一个左值,该值指定了可以用寄存器存储类声明的自动存储持续时间的对象,但是该对象未初始化。(6.3.2.1)。
本附件确实导致了这样的解释,即void在评估过程中包含未定义行为的表达式实际上并未得到评估,但是由于它只是附件,因此我不确定其论据的重要性。