簡體   English   中英

防止將uint64_t轉換為uint16_t

[英]Prevent converting uint64_t to uint16_t

為什么以下代碼在clang ++中編譯?

是否有任何c ++標志來防止這種情況發生 - 我希望編譯器拋出錯誤,因為我將std :: uint64_t作為參數傳遞給接受std :: uint16_t的函數。

#include <cstdint>
using namespace std;

void foo(uint16_t x) {
}

int main() {
    uint64_t x = 10000;
    foo(x);
    return 0;
}

你可以刪除c ++ 11中的函數

void foo(uint64_t) = delete;

它通過在函數重載分辨率下添加簽名來工作,如果它是更好的匹配,則會發生錯誤。 您也可以使其通用,只允許您原始簽名

template <class T> void foo( T&& ) = delete;

您還可以使用enable_if作為SFINAE返回參數

#include <iostream>
#include <cstdint>
#include <type_traits>

template<typename T>
typename std::enable_if<std::is_same<T, uint16_t>::value>::type 
foo(T x) 
{
    std::cout << "uint16_t" << std::endl;
}

template<typename T>
typename std::enable_if<!std::is_same<T, uint16_t>::value>::type 
foo(T x)
{
    std::cout << "rest" << std::endl;
}

int main() {
    uint16_t x = 10000;
    uint64_t y = 100000;
    foo(x); // picks up uint16_t version
    foo(y); // picks up anything else, but NOT uint16_t
    return 0;
}

通過這種方式,您可以擁有一個專門處理uint16_t重載,以及另一個處理其他任何uint16_t重載。

這是一個允許擴大轉換並防止縮小轉換的解決方案:

#include <cstdint>
#include <type_traits>

void foo(uint16_t x) {
}

template <class T>
typename std::enable_if<sizeof(uint16_t) < sizeof(T)>::type foo(const T& t) = delete;

int main() {
    uint64_t x = 10000;
    uint16_t y = 10000;
    uint8_t z = 100;
    // foo(x); // ERROR: narrowing conversion
    foo(y); // OK: no conversion
    foo(z); // OK: widening conversion
    return 0;
}

如果您還想禁止帶有簽名類型參數的調用(有符號和無符號類型之間的轉換不是“無損”),您可以使用以下聲明:

#include <cstdint>
#include <type_traits>

void foo(uint16_t x) {
}

template <class T>
typename std::enable_if<(sizeof(uint16_t) < sizeof(T)) ||
                        (std::is_signed<T>::value != std::is_signed<uint16_t>::value)
                       >::type
foo(const T& t) = delete;

int main() {
    uint64_t u64 = 10000;
    uint16_t u16 = 10000;
    uint8_t u8 = 100;
    int64_t s64 = 10000;
    int16_t s16 = 10000;
    int8_t s8 = 100; 

    //foo(u64); // ERROR: narrowing conversion
    foo(u16); // OK: no conversion
    foo(u8); // OK: widening conversion
    //foo(s64); // ERROR: narrowing conversion AND signed/unsigned mismatch
    //foo(s16); // ERROR: signed/unsigned mismatch
    //foo(s8); // ERROR: signed/unsigned mismatch

    return 0;
}

如果您想允許擴大轉化次數,但禁止縮小轉化次數,可能:

void foo(uint16_t x) {
}

template <class T>
void foo( const T&& t )
{
    return foo(uint16_t{t});
}

這會強制除uint16_t之外的所有類型都經過列表初始化,這禁止縮小轉換。

但是,如果你已經有很多重載,它就不能很好地工作。

雖然這里的大多數答案在技術上都是正確的,但您很可能不希望該行為僅適用於此函數,因此您必須為每個轉換案例編寫的“代碼級”解決方案可能不是您想要的。

在“項目/編譯級別”,您可以添加此標志以警告您這些轉換:

-Wconversion

或者如果您喜歡直接將它們視為錯誤:

-Werror=conversion

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM