溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

總結(jié)C++98/11/17表達(dá)式類別

發(fā)布時(shí)間:2020-07-22 10:38:05 來源:億速云 閱讀:229 作者:小豬 欄目:編程語言

小編這次要給大家分享的是總結(jié)C++98/11/17表達(dá)式類別,文章內(nèi)容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。

目標(biāo)

以下代碼能否編譯通過,能否按照期望運(yùn)行?

#include <utility>
#include <type_traits>

namespace cpp98
{

struct A { };
A func() { return A(); }

int main()
{
 int i = 1;
 i = 2;
 // 3 = 4;
 const int j = 5;
 // j = 6;
 i = j;
 func() = A();
 return 0;
}

}

namespace cpp11
{

#define is_lvalue(x) std::is_lvalue_reference<decltype((x))>::value
#define is_prvalue(x) !std::is_reference<decltype((x))>::value
#define is_xvalue(x) std::is_rvalue_reference<decltype((x))>::value
#define is_glvalue(x) (is_lvalue(x) || is_xvalue(x))
#define is_rvalue(x) (is_xvalue(x) || is_prvalue(x))

void func();
int non_reference();
int&& rvalue_reference();
std::pair<int, int> make();

struct Test
{
 int field;
 void member_function()
 {
 static_assert(is_lvalue(field), "");
 static_assert(is_prvalue(this), "");
 }
 enum Enum
 {
 ENUMERATOR,
 };
};

int main()
{
 int i;
 int&& j = std::move(i);
 Test test;

 static_assert(is_lvalue(i), "");
 static_assert(is_lvalue(j), "");
 static_assert(std::is_rvalue_reference<decltype(j)>::value, "");
 static_assert(is_lvalue(func), "");
 static_assert(is_lvalue(test.field), "");
 static_assert(is_lvalue("hello"), "");

 static_assert(is_prvalue(2), "");
 static_assert(is_prvalue(non_reference()), "");
 static_assert(is_prvalue(Test{3}), "");
 static_assert(is_prvalue(test.ENUMERATOR), "");

 static_assert(is_xvalue(rvalue_reference()), "");
 static_assert(is_xvalue(make().first), "");

 return 0;
}

}

namespace reference
{

int&& rvalue_reference()
{
 int local = 1;
 return std::move(local);
}

const int& const_lvalue_reference(const int& arg)
{
 return arg;
}

int main()
{
 auto&& i = rvalue_reference(); // dangling reference
 auto&& j = const_lvalue_reference(2); // dangling reference
 int k = 3;
 auto&& l = const_lvalue_reference(k);
 return 0;
}

}

namespace auto_decl
{

int non_reference() { return 1; }
int& lvalue_reference() { static int i; return i; }
const int& const_lvalue_reference() { return lvalue_reference(); }
int&& rvalue_reference() { static int i; return std::move(i); }

int main()
{
 auto [s1, s2] = std::pair(2, 3);
 auto&& t1 = s1;
 static_assert(!std::is_reference<decltype(s1)>::value);
 static_assert(std::is_lvalue_reference<decltype(t1)>::value);

 int i1 = 4;
 auto i2 = i1;
 decltype(auto) i3 = i1;
 decltype(auto) i4{i1};
 decltype(auto) i5 = (i1);
 static_assert(!std::is_reference<decltype(i2)>::value, "");
 static_assert(!std::is_reference<decltype(i3)>::value, "");
 static_assert(!std::is_reference<decltype(i4)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(i5)>::value, "");

 auto n1 = non_reference();
 decltype(auto) n2 = non_reference();
 auto&& n3 = non_reference();
 static_assert(!std::is_reference<decltype(n1)>::value, "");
 static_assert(!std::is_reference<decltype(n2)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(n3)>::value, "");

 auto l1 = lvalue_reference();
 decltype(auto) l2 = lvalue_reference();
 auto&& l3 = lvalue_reference();
 static_assert(!std::is_reference<decltype(l1)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(l2)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(l3)>::value, "");

 auto c1 = const_lvalue_reference();
 decltype(auto) c2 = const_lvalue_reference();
 auto&& c3 = const_lvalue_reference();
 static_assert(!std::is_reference<decltype(c1)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(c2)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(c3)>::value, "");

 auto r1 = rvalue_reference();
 decltype(auto) r2 = rvalue_reference();
 auto&& r3 = rvalue_reference();
 static_assert(!std::is_reference<decltype(r1)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(r2)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(r3)>::value, "");

 return 0;
}

}

namespace cpp17
{

class NonMoveable
{
public:
 int i = 1;
 NonMoveable(int i) : i(i) { }
 NonMoveable(NonMoveable&&) = delete;
};

NonMoveable make(int i)
{
 return NonMoveable{i};
}

void take(NonMoveable nm)
{
 return static_cast<void>(nm);
}

int main()
{
 auto nm = make(2);
 auto nm2 = NonMoveable{make(3)};
 // take(nm);
 take(make(4));
 take(NonMoveable{make(5)});
 return 0;
}

}

int main()
{
 cpp98::main();
 cpp11::main();
 reference::main();
 auto_decl::main();
 cpp17::main();
}

C++98表達(dá)式類別

每個(gè)C++表達(dá)式都有一個(gè)類型:42的類型為int,int i;則(i)的類型為int&。這些類型落入若干類別中。在C++98/03中,每個(gè)表達(dá)式都是左值或右值。

左值(lvalue)是指向真實(shí)儲(chǔ)存在內(nèi)存或寄存器中的值的表達(dá)式?!發(fā)”指的是“l(fā)eft-hand side”,因?yàn)樵贑中只有l(wèi)value才能寫在賦值運(yùn)算符的左邊。相對(duì)地,右值(rvalue,“r”指的是“right-hand side”)只能出現(xiàn)在賦值運(yùn)算符的右邊。

有一些例外,如const int i;,i雖然是左值但不能出現(xiàn)在賦值運(yùn)算符的左邊。到了C++,類類型的rvalue卻可以出現(xiàn)在賦值運(yùn)算符的左邊,事實(shí)上這里的賦值是對(duì)賦值運(yùn)算符函數(shù)的調(diào)用,與基本類型的賦值是不同的。

lvalue可以理解為可取地址的值,變量、對(duì)指針解引用、對(duì)返回類型為引用類型的函數(shù)的調(diào)用等,都是lvalue。臨時(shí)對(duì)象都是rvalue,包括字面量和返回類型為非引用類型的函數(shù)調(diào)用等。字符串字面量是個(gè)例外,它屬于不可修改的左值。

賦值運(yùn)算符左邊需要一個(gè)lvalue,右邊需要一個(gè)rvalue,如果給它一個(gè)lvalue,該lvalue會(huì)被隱式轉(zhuǎn)換成rvalue。這個(gè)過程是理所當(dāng)然的。

動(dòng)機(jī)

C++11引入了右值引用和移動(dòng)語義。函數(shù)返回的右值引用,顧名思義,應(yīng)該表現(xiàn)得和右值一樣,但是這會(huì)破壞很多既有的規(guī)則:

  • rvalue是匿名的,不一定有存儲(chǔ)空間,但右值引用指向內(nèi)存中的具體對(duì)象,該對(duì)象還要被維護(hù)著;
  • rvalue的類型是確定的,必須是完全類型,靜態(tài)類型與動(dòng)態(tài)類型相同,而右值引用可以是不完全類型,也可以支持多態(tài);
  • 非類類型的rvalue沒有cv修飾(const和volatile),但右值引用可以有,而且修飾符必須保留。

這給傳統(tǒng)的lvalue/rvalue二分法帶來了挑戰(zhàn),C++委員會(huì)面臨選擇:

  • 維持右值引用是rvalue,添加一些特殊規(guī)則;
  • 把右值引用歸為lvalue,添加一些特殊規(guī)則;
  • 細(xì)化表達(dá)式類別。

上述問題只是冰山一角;歷史選擇了第三種方案。

C++11表達(dá)式類別

C++11提出了表達(dá)式類別(value category)的概念。雖然名叫“value category”,但類別劃分的是表達(dá)式而不是值,所以我從標(biāo)題開始就把它譯為“表達(dá)式類別”。C++標(biāo)準(zhǔn)定義表達(dá)式為:

An expression is a sequence of operators and operands that specifies a computation. An expression can result in a value and can cause side effects.

每個(gè)表達(dá)式都是三種類別之一:左值(lvalue)、消亡值(xvalue)和純右值(prvalue),稱為主類別。還有兩種混合類別:lvalue和xvalue統(tǒng)稱范左值(glvalue),xvalue和prvalue統(tǒng)稱右值(rvalue)。

總結(jié)C++98/11/17表達(dá)式類別

#define is_glvalue(x) (is_lvalue(x) || is_xvalue(x))
#define is_rvalue(x) (is_xvalue(x) || is_prvalue(x))

C++11對(duì)這些類別的定義如下:

  • lvalue指定一個(gè)函數(shù)或一個(gè)對(duì)象;
  • xvalue(eXpiring vavlue)也指向?qū)ο螅ǔ=咏渖芷诘慕K點(diǎn);一些涉及右值引用的表達(dá)式的結(jié)果是xvalue;
  • gvalue(generalized lvalue)是一個(gè)lvalue或xvalue;
  • rvalue是xvalue、臨時(shí)對(duì)象或它們的子對(duì)象,或者沒有關(guān)聯(lián)對(duì)象的值;
  • prvalue(pure rvalue)是不是xvalue的rvalue。

這種定義不是很清晰。具體來講,lvalue包括:(點(diǎn)擊展開)

lvalue的性質(zhì):

  • 與glvalue相同;
  • 內(nèi)置取地址運(yùn)算符可以作用于lvalue;
  • 可修改的lvalue可以出現(xiàn)在內(nèi)置賦值運(yùn)算符的左邊;
  • 可以用來初始化一個(gè)左值引用。

prvalue包括:

prvalue的性質(zhì):

  • 與rvalue相同;
  • 不能是多態(tài)的;
  • 非類類型且非數(shù)組的prvalue沒有cv修飾符,即使寫了也沒有;
  • 必須是完全類型;
  • 不能是抽象類型或其數(shù)組。

xvalue包括:

xvalue的性質(zhì);

  • 與rvalue相同;
  • 與glvalue相同。

glvalue的性質(zhì):

  • 可以隱式轉(zhuǎn)換為prvalue;
  • 可以是多態(tài)的;
  • 可以是不完全類型。

rvalue的性質(zhì):

  • 內(nèi)置取地址運(yùn)算符不能作用于rvalue;
  • 不能出現(xiàn)在內(nèi)置賦值或復(fù)合賦值運(yùn)算符的左邊;
  • 可以綁定給const左值引用(見下);
  • 可以用來初始化右值引用(見下);
  • 如果一個(gè)函數(shù)有右值引用參數(shù)和const左值引用參數(shù)兩個(gè)重載,傳入一個(gè)rvalue時(shí),右值引用的那個(gè)重載被調(diào)用。

還有一些特殊的分類:

  • 對(duì)于非靜態(tài)成員函數(shù)mf及其指針pmf,a.mf、p->mf、a.*pmf和p->*pmf都被歸類為prvalue,但它們不是常規(guī)的prvalue,而是pending(即將發(fā)生的) member function call,只能用于函數(shù)調(diào)用;
  • 返回void的函數(shù)調(diào)用、向void的類型裝換和throw語句都是void表達(dá)式,不能用于初始化引用或函數(shù)參數(shù);
  • C++中最小的尋址單位是字節(jié),因此位域不能綁定到非const左值引用上;const左值引用和右值引用可以綁定位域,它們指向的是位域的一個(gè)拷貝。

終于把5個(gè)類別介紹完了。表達(dá)式可以分為lvalue、xvalue和prvalue三類,lvalue和prvalue與C++98中的lvalue和rvalue類似,而xvalue則完全是為右值引用而生,兼有g(shù)lvalue與rvalue的性質(zhì)。除了這種三分類法外,表達(dá)式還可以分為lvalue和rvalue兩類,它們之間的主要差別在于是否可以取地址;還可以分為glvalue和prvalue兩類,它們之間的主要差別在于是否存在實(shí)體,glvalue有實(shí)體,因而可以修改原對(duì)象,xvalue常被壓榨剩余價(jià)值。

引用綁定

我們稍微岔開一會(huì),來看兩個(gè)與表達(dá)式分類相關(guān)的特性。

引用綁定有以下類型:

  • 左值引用綁定lvalue,cv修飾符只能多不能少;
  • 右值引用可以綁定rvalue,我們通常不給右值引用加cv修飾符;
  • const左值引用可以綁定rvalue。

左值引用綁定lvalue天經(jīng)地義,沒什么需要關(guān)照的。但rvalue都是臨時(shí)對(duì)象,綁定給引用就意味著要繼續(xù)用它,它的生命周期會(huì)受到影響。通常,rvalue的生命周期會(huì)延長(zhǎng)到綁定引用的聲明周期,但有以下例外:

  • 由return語句返回的臨時(shí)對(duì)象在return語句結(jié)束后即銷毀,這樣的函數(shù)總是會(huì)返回一個(gè)空懸引用(dangling reference);
  • 綁定到初始化列表中的引用的臨時(shí)對(duì)象的生命周期只延長(zhǎng)到構(gòu)造函數(shù)結(jié)束——這是個(gè)缺陷,在C++14中被修復(fù);
  • 綁定到函數(shù)參數(shù)的臨時(shí)對(duì)象的生命周期延長(zhǎng)到函數(shù)調(diào)用所在表達(dá)式結(jié)束,把該參數(shù)作為引用返回會(huì)得到空懸引用;
  • 綁定到new表達(dá)式中的引用的臨時(shí)對(duì)象的生命周期只延長(zhǎng)到包含new的表達(dá)式的結(jié)束,不會(huì)跟著那個(gè)對(duì)象。

簡(jiǎn)而言之,臨時(shí)變量的生命周期只能延長(zhǎng)一次。

#include <utility>

int&& rvalue_reference()
{
 int local = 1;
 return std::move(local);
}

const int& const_lvalue_reference(const int& arg)
{
 return arg;
}

int main()
{
 auto&& i = rvalue_reference(); // dangling reference
 auto&& j = const_lvalue_reference(2); // dangling reference
 int k = 3;
 auto&& l = const_lvalue_reference(k);
}

rvalue_reference返回一個(gè)指向局部變量的引用,因此i是空懸引用;2綁定到const_lvalue_reference的參數(shù)arg上,函數(shù)返回后延長(zhǎng)的生命周期達(dá)到終點(diǎn),因此j也是懸空引用;k在傳參的過程中根本沒有臨時(shí)對(duì)象創(chuàng)建出來,所以l不是空懸引用,它是指向k的const左值引用。

auto與decltype

從C++11開始,auto關(guān)鍵字用于自動(dòng)推導(dǎo)類型,用的是模板參數(shù)推導(dǎo)的規(guī)則:如果是拷貝列表初始化,則對(duì)應(yīng)模板參數(shù)為std::initializer_list<T>,否則把a(bǔ)uto替換為T。至于詳細(xì)的模板參數(shù)推導(dǎo)規(guī)則,要介紹的話未免喧賓奪主了。

還好,這不是我們的重點(diǎn)。在引出重點(diǎn)之前,我們還得先看decltype。

decltype用于聲明一個(gè)類型("declare type"),有兩種語法:

  • decltype(entity);
  • decltype(expression)。

第一種,decltype的參數(shù)是沒有括號(hào)包裹的標(biāo)識(shí)符或類成員,則decltype產(chǎn)生該實(shí)體的類型;如果是結(jié)構(gòu)化綁定,則產(chǎn)生被引類型。

第二種,decltype的參數(shù)是不能匹配第一種的任何表達(dá)式,其類型為T,則根據(jù)其表達(dá)式類別討論:

  • 如果是xvalue,產(chǎn)生T&&——#define is_xvalue(x) std::is_rvalue_reference<decltype((x))>::value;
  • 如果是lvalue,產(chǎn)生T&——#define is_lvalue(x) std::is_lvalue_reference<decltype((x))>::value;
  • 如果是prvalue,產(chǎn)生T——#define is_prvalue(x) !std::is_reference<decltype((x))>::value。

因此,decltype(x)和decltype((x))產(chǎn)生的類型通常是不同的。

對(duì)于不帶引用修飾的auto,初始化器的表達(dá)式類別會(huì)被抹去,為此C++14引入了新語法decltype(auto),產(chǎn)生的類型為decltype(expr),其中expr為初始化器。對(duì)于局部變量,等號(hào)右邊加上一對(duì)圓括號(hào),可以保留表達(dá)式類別。

#include <utility>
#include <type_traits>

int non_reference() { return 1; }
int& lvalue_reference() { static int i; return i; }
const int& const_lvalue_reference() { return lvalue_reference(); }
int&& rvalue_reference() { static int i; return std::move(i); }

int main()
{
 auto [s1, s2] = std::pair(2, 3);
 auto&& t1 = s1;
 static_assert(!std::is_reference<decltype(s1)>::value);
 static_assert(std::is_lvalue_reference<decltype(t1)>::value);

 int i1 = 4;
 auto i2 = i1;
 decltype(auto) i3 = i1;
 decltype(auto) i4{i1};
 decltype(auto) i5 = (i1);
 static_assert(!std::is_reference<decltype(i2)>::value);
 static_assert(!std::is_reference<decltype(i3)>::value);
 static_assert(!std::is_reference<decltype(i4)>::value);
 static_assert(std::is_lvalue_reference<decltype(i5)>::value);

 auto n1 = non_reference();
 decltype(auto) n2 = non_reference();
 auto&& n3 = non_reference();
 static_assert(!std::is_reference<decltype(n1)>::value, "");
 static_assert(!std::is_reference<decltype(n2)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(n3)>::value, "");

 auto l1 = lvalue_reference();
 decltype(auto) l2 = lvalue_reference();
 auto&& l3 = lvalue_reference();
 static_assert(!std::is_reference<decltype(l1)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(l2)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(l3)>::value, "");

 auto c1 = const_lvalue_reference();
 decltype(auto) c2 = const_lvalue_reference();
 auto&& c3 = const_lvalue_reference();
 static_assert(!std::is_reference<decltype(c1)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(c2)>::value, "");
 static_assert(std::is_lvalue_reference<decltype(c3)>::value, "");

 auto r1 = rvalue_reference();
 decltype(auto) r2 = rvalue_reference();
 auto&& r3 = rvalue_reference();
 static_assert(!std::is_reference<decltype(r1)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(r2)>::value, "");
 static_assert(std::is_rvalue_reference<decltype(r3)>::value, "");
}

用auto定義的變量都是int類型,無論函數(shù)的返回類型的引用和const修飾;用decltype(auto)定義的變量的類型與函數(shù)返回類型相同;auto&&是轉(zhuǎn)發(fā)引用,n3類型為int&&,其余與decltype(auto)相同。

C++17表達(dá)式類別

眾所周知,編譯器常會(huì)執(zhí)行NRVO(named return value optimization),減少一次對(duì)函數(shù)返回值的移動(dòng)或拷貝。不過,這屬于C++標(biāo)準(zhǔn)說編譯器可以做的行為,卻沒有保證編譯器會(huì)這么做,因此客戶不能對(duì)此作出假設(shè),從而需要提供一個(gè)拷貝或移動(dòng)構(gòu)造函數(shù),盡管它們可能不會(huì)被調(diào)用。然而,并不是所有情況下都能提供移動(dòng)構(gòu)造函數(shù),即使能移動(dòng)構(gòu)造函數(shù)也未必只是一個(gè)指針的交換??傊覀兠髦苿?dòng)構(gòu)造函數(shù)不會(huì)被調(diào)用卻還要硬著頭皮提供一個(gè),這樣做非常形式主義。

所以,C++17規(guī)定了拷貝省略,確保在以下情況下,即使拷貝或移動(dòng)構(gòu)造函數(shù)有可觀察的效果,它們也不會(huì)被調(diào)用,原本要拷貝或移動(dòng)的對(duì)象直接在目標(biāo)位置構(gòu)造:

  • 在return表達(dá)式中,運(yùn)算數(shù)是忽略cv修飾符以后的返回類型的prvalue;
  • 在初始化中,初始化器是與變量相同類型的prvalue。

值得一提的是,這類行為在C++17中不能算是一種優(yōu)化,因?yàn)椴淮嬖谟脕砜截惢蛞苿?dòng)的臨時(shí)對(duì)象。事實(shí)上,C++17重新定義了表達(dá)式類別:

  • glvalue的求值能確定對(duì)象、位域、函數(shù)的身份;
  • prvalue的求值初始化對(duì)象或位域,或計(jì)算運(yùn)算數(shù)的值,由上下文決定;
  • xvalue是表示一個(gè)對(duì)象或位域的資源能被重用的glvalue;
  • lvalue是不是xvalue的glvalue;
  • rvalue是prvalue或xvalue。

這個(gè)定義在功能上與C++11中的相同,但是更清晰地指出了glvalue和prvalue的區(qū)別——glvalue產(chǎn)生地址,prvalue執(zhí)行初始化。

prvalue初始化的對(duì)象由上下文決定:在拷貝省略的情形下,prvalue不曾有關(guān)聯(lián)的對(duì)象;其他情形下,prvalue將產(chǎn)生一個(gè)臨時(shí)對(duì)象,這個(gè)過程稱為臨時(shí)實(shí)體化(temporary materialization)。

臨時(shí)實(shí)體化把一個(gè)完全類型的prvalue轉(zhuǎn)換成xvalue,在以下情形中發(fā)生:

  • 把引用綁定到prvalue上;
  • 類prvalue被獲取成員;
  • 數(shù)組prvalue被轉(zhuǎn)換為指針或下標(biāo)取元素;
  • prvalue出現(xiàn)在大括號(hào)初始化列表中,用于初始化一個(gè)std::initializer_list<T>;
  • 被使用typeid或sizeof運(yùn)算符;
  • 在語句expr;中或被轉(zhuǎn)換成void,即該表達(dá)式的值被丟棄。

或者可以理解為,所有非拷貝省略的場(chǎng)合中的prvalue都會(huì)被臨時(shí)實(shí)體化。

class NonMoveable
{
public:
 int i = 1;
 NonMoveable(int i) : i(i) { }
 NonMoveable(NonMoveable&&) = delete;
};

NonMoveable make(int i)
{
 return NonMoveable{i};
}

void take(NonMoveable nm)
{
 return static_cast<void>(nm);
}

int main()
{
 auto nm = make(2);
 auto nm2 = NonMoveable{make(3)};
 // take(nm);
 take(make(4));
 take(NonMoveable{make(5)});
}

NonMoveable的移動(dòng)構(gòu)造函數(shù)被聲明為delete,于是拷貝構(gòu)造函數(shù)也被隱式delete。在auto nm = make(2);中,NonMoveable{i}為prvalue,根據(jù)拷貝省略的第一條規(guī)則,它直接構(gòu)造為返回值;返回值是NonMoveable的prvalue,與nm類型相同,根據(jù)第二條規(guī)則,這個(gè)prvalue直接在nm的位置上構(gòu)造;兩部分結(jié)合,該聲明式相當(dāng)于NonMoveable nm{2};。

在MSVC中,這段代碼不能通過編譯,這是編譯器未能嚴(yán)格遵守C++標(biāo)準(zhǔn)的緣故。然而,如果在NonMoveable的移動(dòng)構(gòu)造函數(shù)中添加輸出語句,程序運(yùn)行起來也沒有任何輸出,即使在Debug模式下、即使用C++11標(biāo)準(zhǔn)編譯都如此。這也側(cè)面反映出拷貝省略的意義。

總結(jié)

C++11規(guī)定每個(gè)表達(dá)式都屬于lvalue、xvalue和prvalue三個(gè)類別之一,表達(dá)式另可分為lvalue和rvalue,或glvalue和prvalue。返回右值引用的函數(shù)調(diào)用是xvalue,右值引用類型的變量是lvalue。

const左值引用和右值引用可以綁定臨時(shí)對(duì)象,但是臨時(shí)對(duì)象的聲明周期只能延長(zhǎng)一次,返回一個(gè)指向局部變量的右值引用也會(huì)導(dǎo)致空懸引用。

標(biāo)識(shí)符加上一對(duì)圓括號(hào)成為表達(dá)式,decltype用于表達(dá)式可以根據(jù)其類別產(chǎn)生相應(yīng)的類型,用decltype(auto)聲明變量可以保留表達(dá)式類別。

C++17中prvalue是否有關(guān)聯(lián)對(duì)象由上下文決定,拷貝省略規(guī)定了特定情況下對(duì)象不經(jīng)拷貝或移動(dòng)直接構(gòu)造,NRVO成為強(qiáng)制性標(biāo)準(zhǔn),使不能被移動(dòng)的對(duì)象在語義上可以值傳遞。

看完這篇關(guān)于總結(jié)C++98/11/17表達(dá)式類別的文章,如果覺得文章內(nèi)容寫得不錯(cuò)的話,可以把它分享出去給更多人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI