2017-09-01 39 views
1

我可以使用模板和刪除功能來防止調用具有字符或浮點變量的階乘,如下所示。如何爲具有負面參數的階乘編寫刪除函數?使用刪除來防止在C++中使用無效值調用14

template <typename T> 
constexpr T factorial(T n) 
{ 
    return (n == 1 || n == 0) ? 1 : (n * factorial(n - 1)); 
} 

constexpr float factorial(double) = delete; 
constexpr char factorial(char) = delete; 

int main() 
{ 
    constexpr auto fiveFactorial = factorial(5); 
    constexpr auto point5fact = factorial(0.5); // Error. Call to deleted version 
    constexpr auto letter5fact = factorial('5'); // DITTO 
    constexpr auto minusFact = factorial(-1); // How to prevent this using delete? 
} 
+2

您不能,因爲值的計算是* runtime *。對於類型來說沒關係,因爲它們在編譯時是已知的,但是對於僅在運行時才知道的事物,您不能得到編譯器錯誤。我能看到的唯一解決方案是確保'T'是* unsigned *類型。 –

+0

作爲另一種可能的解決方案,如果你只想允許無符號整數類型,只是* declare *通用函數,但不實現它(或者甚至可能將其標記爲已刪除?)。然後使用定義(實現)專門化無符號整數類型的函數。 –

回答

4

不可能。 = delete是編譯時的東西,而你的參數在編譯時並不總是知道的。

您可以使用unsigned參數代替刪除所有已刪除的過載,代價是無法使用帶符號的數字調用您的函數,如factorial(2)

template <typename T> constexpr T factorial(T n) 
{ 
    static_assert(std::is_unsigned_v<T> && !std::is_same_v<T, char>, 
        "Parameter type must be integral, unsigned, and not `char`."); 
    return (n == 1 || n == 0) ? 1 : (n * factorial(T(n - 1))); 
} 
+0

當然,OP的'factorial(5)'不再適用於此。如果OP沒問題,那很好,但我認爲它可以使用拼寫。 – hvd

+0

@ hvd謝謝,編輯。我只是忽略了這一點。 – HolyBlackCat

+0

但是現在需要調整實現:遞歸調用必須是「factorial(n-1u)」,否則調用'factorial(5us)'(調用一個無符號短值5)將失敗,因爲'5us - 1 '是'int'類型,它不是無符號的。 –

1

如何編寫負參數階乘刪除功能?

您不能,因爲參數的值是一個運行屬性,但您只能刪除基於類型的重載。

但是由於這是constexpr,還有另一種方法 - 只是在常量表達式中對負輸入進行函數形成。例如,通過投擲。此外,由於您標記了此C++ 14,因此我正在將您的實現更改爲循環:

template <typename T> 
constexpr T factorial(T n) 
{ 
    if (n < 0) throw std::runtime_error("bad user!"); 

    T product = 1; 
    for (T i = 2; i <= n; ++i) { 
     product *= i; 
    } 
    return product; 
} 

constexpr auto good = factorial(5); // fine 
constexpr auto bad = factorial(-1); // compile error because factorial(-1) 
            // is not a valid constant expression