2010-02-20 45 views
0

請考慮下面的代碼。const正確性和返回值 - C++

struct foo 
{ 
}; 

template<typename T> 
class test 
{ 
public: 

    test() {} 

    const T& value() const 
    { 
     return f; 
    } 

private: 
    T f; 
}; 


int main() 
{ 
    const test<foo*> t; 
    foo* f = t.value(); 
    return 0; 
} 

tconst變量和value()是常數成員函數,它返回const T&。 AFAIK,const類型不能分配給非常量類型。但foo* f = t.value();如何編譯好。這是怎麼回事,我如何確保value()只能分配到const foo*

編輯

我發現,這是在使用模板上發生的情況。以下代碼按預期工作。

class test 
{ 
public: 

    test() {} 

    const foo* value() const { return f; } 

private: 
    foo* f; 
}; 


int main() 
{ 
    const test t; 
    foo* f = t.value(); // error here 
    return 0; 
} 

爲什麼使用模板時會出現問題?

回答

6

因爲您有兩個級別的間接 - 在您的主函數中,對value的調用返回對非const的指針foo的const指針的引用。

這可以安全地複製到非常量foo的非常量指針中。

如果你已經實例化testconst foo *,這將是一個不同的故事。

const test<const foo*> t; 
foo* f = t.value(); // error 
const foo* f = t.value(); // fine 
return 0; 

更新

從評論:

值()返回常量Ť&可 只能分配給另一個常量 類型。但在這種情況下,安全地允許編譯器 進行轉換。

只能讀取Const數據。它不能寫(「變異」)。但是複製一些數據是一種閱讀方式,所以沒關係。例如:

const int c = 5; 
int n = c; 

在這裏,我在c有一些常量數據,以及我的數據複製到一個非const變量n。沒關係,只是讀取數據。 c中的值尚未修改。

現在,假設你foo了一些數據吧:

struct foo { int n; }; 

如果我有一個非const指針指向其中的一個,我可以通過指針修改n值。你問你的test模板存儲一個指向非const的指針foo,然後做了一個const實例test。因此,只有指針地址是不變的。沒有人可以更改存儲在test內的指針中的地址,因此無法指向另一個對象。但是,它指向的對象可以修改其內容。

更新2:

當你做的例子您的非模板版本,你犯了一個錯誤。爲了做到這一點,您需要將foo *替換爲每個有T的地方。

const T& value() const 

請注意,您有一個對const T的引用。所以返回值將是對const的引用:a foo *。這只是指針地址,無法修改。它指向的對象可以修改其內容。

在第二個示例中,您擺脫了引用部分,它改變了含義並使const修飾符適用於指針指向的對象,而不是應用於指針本身。

+0

謝謝。但我還不夠清楚。國際海事組織的意見,'價值()'返回'const T&'只能被分配給另一個'const'類型。但在這種情況下,編譯器安全地允許轉換。另外在我的情況下,我不能'測試'。 – 2010-02-20 18:49:50

+0

@Appu這是指向常量的指針和指向非常量的常量指針的區別。不知道你爲什麼不能「測試」。 – 2010-02-20 18:59:23

+0

請參閱編輯。我有一個非const成員'f'並且從它返回'const'。在這種情況下,編譯正確地抱怨。看起來使用模板時行爲不同。 – 2010-02-20 19:02:09

1

使用下面的模板專業化:

template<typename T> 
class test<T*> 
{ 
public: 

    test() {} 

    const T* value() const 
    { 
     return f; 
    } 

private: 
    T* f; 
}; 

包括在此之後,G ++說:

d.cpp: In function ‘int main()’: 
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’ 
1

沒有什麼錯在你的代碼中,有一個常量引用指針只意味着你可以不修改指針,但指向的對象保持完美可變。如果您在main函數中嘗試更改f成員t指向的地址,則會看到您不能:封裝完好無損。

這是同樣的原理,使下面的代碼有效:

void foo(std::vector<int *> const & v) 
{ 
    *v[0] = 0; // op. [] returns const & to int * 
} 

人們新的C++,通常是由這種行爲感到驚訝,因爲他們的一個常量矢量不應允許其內容的修改。事實上它並不存在,因爲存儲在向量中的指針不會改變(它始終指向相同的地址)。這是修改過的指向對象,但矢量並不關心它。

唯一的解決辦法就是像艾米特說的那樣做,併爲T*提供專業化的班級。