2017-08-03 43 views
10

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:

  1. 範圍枚舉類型(7.2)的值可以顯式轉換爲整型。如果原始值可以用指定的類型表示,則值不變。 ...
  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>,上述財產應持有。

    這個論證是否成立,或者我錯過了標準中的一些奇怪的子句,這樣在枚舉的底層類型中強制轉換可能導致未定義或未指定的行爲?

    +2

    好問題,我當然希望答案是'是永遠安全',因爲我一直在使用helper函數靜態鑄造/從underlying_type 一段時間,沒有給它更多的想法:P – stijn

    +0

    @stijn所以做我用相同的用例。它只是在代碼審查期間提出的。根據7.2和5.2.9,它應該沒問題,但我希望得到其他人的第二意見(或者我在下週結束時再次閱讀標準)。 – Zeta

    回答

    3

    標準似乎決定允許您使用給定類型的任意整數值作爲固定爲該類型的枚舉的值,即使它們未被命名爲枚舉值。 5.2.9.10中的警告大概是爲了限制沒有固定基礎類型的枚舉。該標準沒有將固定類型枚舉值的「範圍」定義爲與枚舉值分開的任何內容。特別是,它說:

    可以定義一個枚舉,其值不是由其任何枚舉​​器定義的枚舉。

    所以「在枚舉值的範圍內」不能被理解爲除「是枚舉值之一」以外的其他任何東西。枚舉值的範圍沒有其他定義。

    因此,對於具有固定基礎類型的枚舉,您是安全的。對於無類型的枚舉,如果你堅持安全的位數,那麼你就是安全的。

    +0

    是不是隻在該範圍內命名常量的枚舉數?我很確定範圍是單獨的概念,但目前無法檢查。 – StoryTeller

    +0

    @StoryTeller「範圍」一詞用於定義(無類型)枚舉的值,但不適用於枚舉具有「範圍」的意義。並且它根本不用於類型枚舉。 – Sneftel

    +0

    好吧,現在我有機會檢查。這就是C++ 17所說的:[「對於基礎類型固定的枚舉,枚舉的值是基礎類型的值。」](https://timsong-cpp.github.io/cppwp/ n4659/dcl.enum#8)。這也適用於根本沒有統計員的枚舉。我不確定C++ 11是否有相同的措辭。 – StoryTeller