![](/img/trans.png)
[英]How can I define operators so that a array of user-defined types can be transformed into an array of primitive types?
[英]Why do primitive and user-defined types act differently when returned as 'const' from a function?
#include <iostream>
using namespace std;
template<typename T>
void f(T&&) { cout << "f(T&&)" << endl; }
template<typename T>
void f(const T&&) { cout << "f(const T&&)" << endl; }
struct A {};
const A g1() { return {}; }
const int g2() { return {}; }
int main()
{
f(g1()); // outputs "f(const T&&)" as expected.
f(g2()); // outputs "f(T&&)" not as expected.
}
問題描述嵌入在代碼中。 我的編譯器是clang 5.0
。
我只是好奇:
在這種情況下,為什么C ++會以不同方式處理內置類型和自定義類型?
我沒有標准的引用,但是cppreference證實了我的懷疑:
非類非數組prvalue不能是cv限定的。 (注意:函數調用或強制轉換表達式可能會導致非類cv限定類型的prvalue,但會立即刪除cv-qualifier。)
返回的const int
只是一個普通的int
prvalue,它使得非const重載比const
更好。
當從函數返回為“const”時,為什么原始類型和用戶定義類型的行為不同?
因為const
部分從函數返回的基本類型中刪除。 原因如下:
在§ 5 Expressions [expr]
C ++ 11中 (p.84):
8
每當glvalue表達式作為操作符的操作數出現時,該操作符需要該操作數的prvalue,左值到右值(4.1),數組到指針(4.2)或函數到指針(4.3)標准轉換是用於將表達式轉換為prvalue。 [注意:因為當表達式轉換為prvalue時,cv-quali firs從非類型表達式的類型中刪除,例如,類型為const int的左值表達式可以在類型為int的prvalue表達式中使用是必須的。 - 尾注]
類似於§ 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
( § 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
):
2
表達式T(),其中T是非數組完整對象類型或(可能是cv-quali fi ed)void類型的簡單類型指定者或typename-speci fi er,它創建了一個特定類型的prvalue,它是有價值的( 8.5;沒有對void()情況進行初始化)。 [注意:如果T是具有cv-quali fi ed的非類型類型,則在確定結果prvalue(3.10)的類型時將忽略cv-quali firs。 - 尾注]
這意味着g2()
返回的const int
prvalue被有效地視為int
。
標准引用,
如果prvalue最初具有類型“cv T”,其中T是cv非限定的非類非數組類型,則在進行任何進一步分析之前將表達式的類型調整為T.
(強調我的)
每當glvalue表達式作為操作符的操作數出現,該操作符需要該操作數的prvalue時,將應用左值到右值,數組到指針或函數到指針的標准轉換來將表達式轉換為prvalue。 [注意:因為當表達式轉換為prvalue時,cv-qualifiers從非類型表達式的類型中刪除,所以例如,可以在類型為
int
的prvalue表達式的情況下使用類型為const int
的左值表達式是必須的。 - 結束說明]
因此對於g2()
, int
是非類型類型,並且(返回值) g2()
是prvalue表達式 ,然后刪除const
限定符,因此返回類型不是const int
,而是int
。 這就是調用f(T&&)
的原因。
以前的答案是完全有效的。 我只想添加一個潛在的動機,為什么它有時可能對返回const對象有用。 在下面的示例中, class A
給出了來自class C
內部數據的視圖,在某些情況下這些內容數據不可修改(免責聲明,為簡潔起見,一些基本部分被省略 - 也可能有更簡單的方法來實現此行為):
class A {
int *data;
friend class C; // allow C to call private constructor
A(int* x) : data(x) {}
static int* clone(int*) {
return 0; /* should actually clone data, with reference counting, etc */
}
public:
// copy constructor of A clones the data
A(const A& other) : data(clone(other.data)) {}
// accessor operators:
const int& operator[](int idx) const { return data[idx]; }
// allows modifying data
int& operator[](int idx) { return data[idx]; }
};
class C {
int* internal_data;
public:
C() : internal_data(new int[4]) {} // actually, requires proper implementation of destructor, copy-constructor and operator=
// Making A const prohibits callers of this method to modify internal data of C:
const A getData() const { return A(internal_data); }
// returning a non-const A allows modifying internal data:
A getData() { return A(internal_data); }
};
int main()
{
C c1;
const C c2;
c1.getData()[0] = 1; // ok, modifies value in c1
int x = c2.getData()[0]; // ok, reads value from c2
// c2.getData()[0] = 2; // fails, tries to modify data from c2
A a = c2.getData(); // ok, calls copy constructor of A
a[0] = 2; // ok, works on a copy of c2's data
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.