const和volatile被称为cv-qualifier由C规格.
specifier和qualifier(cv-qualifier)之间究竟有什么区别?限定符也是一个说明符吗?
是否必须qualifier只有左值?
什么是限定词以外的cv-qualifier?
我的上述理解是否有意义?
在C++ 03中编程时,我们无法将未命名的临时文件T()传递给函数void foo(T&);.通常的解决方案是给临时名称,然后传递它:
T v;
foo(v);
Run Code Online (Sandbox Code Playgroud)
现在,沿着C++ 0x - 现在使用rvalue引用,定义为的函数void foo(T&&)将允许我传递临时值.这让我想到了一个问题:因为一个采用右值引用的函数可以同时使用右值引用(未命名的临时值)以及左值引用(命名为非const引用),是否有理由在函数参数中再使用左值引用?我们不应该总是使用rvalues作为函数参数吗?
当然,一个采用左值引用的函数会阻止调用者传递临时值,但我不确定这是否是一个有用的限制.
我们不能写,int& ref = 40因为我们需要lvalue在右侧.但我们可以写const int& ref = 40.为什么这可能?rvalue而是40lvalue
我知道这是一个例外,但为什么呢?
例子:
typedef enum Color
{
RED,
GREEN,
BLUE
} Color;
void func(unsigned int& num)
{
num++;
}
int main()
{
Color clr = RED;
func(clr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译时出现以下错误:
<source>: In function 'int main()':
<source>:16:9: error: cannot bind non-const lvalue reference of type 'unsigned int&' to an rvalue of type 'unsigned int'
func(clr);
^~~
Run Code Online (Sandbox Code Playgroud)
我认为clr我传递给的变量 ( )func(unsigned int&)是一个左值。我可以获得的地址clr并可以为其分配另一个值。当我尝试将它传递给 时,为什么它会变成右值func(unsigned int&)?
据说这里是
术语可修改的左值用于强调左值允许指定的对象被改变以及被检查.以下对象类型是左值,但不是可修改的左值:
- 数组类型
- 不完整的类型
- const限定类型
- 结构或联合类型,其成员之一被限定为const类型
因为这些左值不可修改,所以它们不能出现在赋值语句的左侧.
为什么数组类型对象不可修改?写不正确
int i = 5, a[10] = {0};
a[i] = 1;
Run Code Online (Sandbox Code Playgroud)
?
而且,什么是不完整的类型?
我有以下代码,我无法工作:
struct foo {};
foo foo1 = {};
template <foo& F>
class FooClass {};
template <foo& F>
void foobar(FooClass<F> arg) {
}
int main() {
FooClass<foo1> f;
foobar(f);
}
Run Code Online (Sandbox Code Playgroud)
错误是:
main.cpp:14:5:错误:没有匹配函数来调用'foobar'
注意:候选模板被忽略:替换失败:推导出的非类型模板参数与其对应的模板参数('foo'vs'foo&')的类型不同
是否可以推断左值参考模板参数?如果是这样,应该怎么做?
在C89标准中,我发现了以下部分:
3.2.2.1左值和函数指示符
除非它是sizeof运算符的操作数,一元&运算符,++运算符, - 运算符或者左运算符.运算符或赋值运算符,没有数组类型的左值将转换为存储在指定对象中的值(并且不再是左值).如果左值具有限定类型,则该值具有左值类型的非限定版本; 否则该值具有左值的类型.如果左值具有不完整类型且没有数组类型,则行为未定义.
如果我正确读取它,它允许我们创建一个lvalue并在其上应用一些运算符,这些运算符在运行时编译并可能导致未定义的行为.
问题是,我想不出一个"不完整类型的左值"的例子,它可以传递编译器的语义检查和触发器undefined behavior.
考虑一个左值是
的左值是表达式(与对象类型或大于空隙以外的不完全类型)指定的一个对象.
那个不完整的类型是
类型被划分为对象类型(描述对象的类型),函数类型(描述函数的类型)和不完整类型(描述对象但缺少确定其大小所需的信息的类型).
我试过一个失败的程序:
struct i_am_incomplete;
int main(void)
{
struct i_am_incomplete *p;
*(p + 1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并得到以下错误:
error: arithmetic on a pointer to an incomplete type 'struct i_am_incomplete'
*(p + 1);
~ ^
Run Code Online (Sandbox Code Playgroud)
任何人都可以想到这方面的例子吗?"不完整类型的左值"的示例,它可以传递编译器的语义检查和触发器undefined behavior.
更新:
正如@algrid在答案中所说,我误解了undefined behavior,其中包含compile error了一个选项.
也许我是鸡蛋里挑骨头,我还在这里想知道的根本动机喜欢undefined behavior过disallowing an lvalue to have an incomplete type.
我认为动态类型意味着动态分配对象使用new.在下面的例子中,您是否p指出动态类型或静态类型的对象?在标准中,它没有说动态类型是动态对象.
1.3.3 - 由左值表达式表示的左值所指的最派生对象(1.8)的类型.[示例:如果指针(8.3.1)p的静态类型是"指向B类的指针"指向D类的对象,从B派生(第10节),则表达式*p的动态类型为"D" ".参考文献(8.3.2)的处理方式相似.]
以下引用的含义是什么呢?
rvalue表达式的动态类型是其静态类型
class Base {
virtual void foo(){}
};
class Derived : public Base {
void foo(){}
};
int main()
{
Derived d;
Base *p = &d;
}
Run Code Online (Sandbox Code Playgroud) 人们通常将"不可修改的"与术语"文字"联系起来
char* str = "Hello World!";
*str = 'B'; // Bus Error!
Run Code Online (Sandbox Code Playgroud)
然而,当使用复合文字时,我很快发现它们是完全可修改的(并且锁定在生成的机器代码上,你会看到它们被推到堆栈上):
char* str = (char[]){"Hello World"};
*str = 'B'; // A-Okay!
Run Code Online (Sandbox Code Playgroud)
我正在编译clang-703.0.29.这两个例子不应该生成完全相同的机器代码吗?如果它是可修改的,复合文字真的是文字吗?
编辑:一个更短的例子是:
"Hello World"[0] = 'B'; // Bus Error!
(char[]){"Hello World"}[0] = 'B'; // Okay!
Run Code Online (Sandbox Code Playgroud) 因此,我想练习的用法,std::forward并创建了Test具有2个构造函数的类。1个带T&,另一个带T&&作为过载。T&打印左值,并T&&打印右值,所以我知道正在使用哪个构造函数。我在堆栈上创建了2个类的实例,令我惊讶的是,这两个实例都使用了T&&重载。
#include <iostream>
#include <type_traits>
#include <utility>
template <class T> auto forward(T &&t) {
if constexpr (std::is_lvalue_reference<T>::value) {
return t;
}
return std::move(t);
}
template <class T> class Test {
public:
Test(T &) { std::cout << "lvalue" << std::endl; };
Test(T &&) { std::cout << "rvalue" << std::endl; };
};
int main() {
int x = 5;
Test<int> a(forward(3));
Test<int> b(forward(x));
return …Run Code Online (Sandbox Code Playgroud)