TL; DR:以下總是安全嗎?還是會導致未定義,未指定或實現定義的行爲?我可以總是安全地投入固定(範圍)枚舉的基礎類型嗎?
template <class T>
using ut = typename std::underlying_type<T>::type;
template <typename E> ut<E> identity(ut<E> value) {
return static_cast<ut<E>>(static_cast<E>(value));
}
如果我有一個範圍的列舉我總是可以撒在基本類型:
#include <cassert> // if you want to follow along
#include <initializer_list> // copy everything and remove my text
enum class priority : int {
low = 0,
normal = 1,
high = 2
};
// works fine
int example = static_cast<int>(priority::high);
對於列舉中定義的所有值我也可以想到的是我得到的值返回:
constexpr priority identity_witness(priority p) {
return static_cast<priority>(static_cast<int>(p));
}
void test_enum() {
for (const auto p : {priority::low, priority::normal, priority::high}) {
assert(p == identity_witness(p));
}
}
根據N3337(C++ 11),5.2.9靜態施放[expr.static.cast]§9-10這是鰭e:
- 範圍枚舉類型(7.2)的值可以顯式轉換爲整型。如果原始值可以用指定的類型表示,則值不變。 ...
- 積分或枚舉類型的值可以顯式轉換爲枚舉類型。如果原始值在枚舉值(7.2)的範圍內,則值不變。 ...
但是,我感興趣的是其他方式。如果我拋出一個枚舉並回到底層類型會發生什麼?
constexpr int identity_witness(int i) {
return static_cast<int>(static_cast<priority>(i));
}
void test_int() {
for (const auto p : {0, 1, 2, 3, 4, 5}) {
assert(p == identity_witness(p));
}
}
int main() {
test_enum();
test_int();
}
這編譯和工作得很好,因爲static_cast
爲基礎類型不會在所有的(可能)更換內存。然而,該標準說行爲是未指定的,如果該值不在範圍:
- [持續否則,所得到的值是不確定的(並且可能無法在該範圍)。
枚舉的範圍是我也不清楚。根據7.2§7,如果枚舉的基本類型是固定的,則「枚舉的值是基礎類型的值」。因此,對於任何std::underlying_type<my_enumeration_type>
,上述財產應持有。
這個論證是否成立,或者我錯過了標準中的一些奇怪的子句,這樣在枚舉的底層類型中強制轉換可能導致未定義或未指定的行爲?
好問題,我當然希望答案是'是永遠安全',因爲我一直在使用helper函數靜態鑄造/從underlying_type一段時間,沒有給它更多的想法:P –
stijn
@stijn所以做我用相同的用例。它只是在代碼審查期間提出的。根據7.2和5.2.9,它應該沒問題,但我希望得到其他人的第二意見(或者我在下週結束時再次閱讀標準)。 – Zeta