[英]const and non-const versions of *static* member functions
我有兩個版本的相同的靜態成員函數:一個采用指向const的指針參數,並采用指向非const的參數。 我想避免代碼重復。
在閱讀了一些堆棧溢出問題(這些都是關於非靜態成員函數的)之后我想出了這個:
class C {
private:
static const type* func(const type* x) {
//long code
}
static type* func(type* x) {
return const_cast<type*>(func(static_cast<const type*>(x)));
}
public:
//some code that uses these functions
};
(我知道玩指針通常是一個壞主意,但我正在實現一個數據結構。)
我在libstdc ++中發現了一些代碼如下:
注意:這些不是成員函數
static type* local_func(type* x)
{
//long code
}
type* func(type* x)
{
return local_func(x);
}
const type* func(const type* x)
{
return local_func(const_cast<type*>(x));
}
在第一種方法中,代碼在一個帶有指針到const參數的函數中。
在第二種方法中,代碼在一個函數中,該函數接受指向非const的參數。
通常應該使用哪種方法? 兩個都正確嗎?
最重要的規則是接口函數(公共方法,詳細命名空間中的一個以外的自由函數等)不應拋棄其輸入的常量。 Scott Meyer是第一個談論使用const_cast防止重復的人之一,這是一個典型的例子( 如何刪除類似的const和非const成員函數之間的代碼重復? ):
struct C {
const char & get() const {
return c;
}
char & get() {
return const_cast<char &>(static_cast<const C &>(*this).get());
}
char c;
};
這是指實例方法而不是靜態/自由函數,但原理是相同的。 您注意到非const版本添加了const來調用另一個方法(對於實例方法, this
指針是輸入)。 然后它最后拋棄了constness; 這是安全的,因為它知道原始輸入不是const。
反過來實現這一點非常危險。 如果你拋棄了你收到的函數參數的constness,如果傳遞給你的對象實際上是const,你在UB中冒了很大的風險。 也就是說,如果你調用任何實際改變對象的方法(現在你已經拋棄const就很容易做到),你可以輕松獲得UB:
C ++標准,第5.2.11 / 7節[const cast]
[注意:根據對象的類型,通過指針,左值或指向數據成員的指針的寫入操作會導致const-qualifier的const_cast,這可能會產生未定義的行為。 - 尾注]
它在私有方法/實現函數中並沒有那么糟糕,因為你可能會仔細控制它被調用的方式/時間,但為什么這樣做呢? 沒有任何好處更危險。
從概念上講,通常情況下,當你有一個相同函數的const和非const版本時,你只是傳遞對象的內部引用( vector::operator[]
是一個規范的例子),而不是實際上是在改變任何東西,這意味着你寫它的方式都是安全的。 但拋棄輸入的常量仍然更危險; 雖然你可能不太可能弄亂自己,想象一個團隊設置,你用錯誤的方式寫它並且工作正常,然后有人改變實現以改變某些東西,給你UB。
總之,在許多情況下,它可能沒有實際的區別,但是有一種正確的方法可以做到這一點,它比替代方案更好: 將 constness 添加到輸入中 ,並從輸出中 刪除 constness。
我以前只見過你的第一個版本,所以根據我的經驗,這是更常見的習語。
第一個版本對我來說似乎是正確的,而第二個版本可能會導致未定義的行為,如果(A)您將實際的const對象傳遞給函數和(B) long code
寫入該對象。 鑒於在第一種情況下,編譯器將告訴您,如果您嘗試寫入對象,我將永遠不會推薦選項2。 您可以考慮使用/返回const的獨立函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.