防止将uint64_t转换为uint16_t

use*_*855 14 c++ clang c++11

为什么以下代码在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;
}
Run Code Online (Sandbox Code Playgroud)

gal*_*p1n 33

你可以在c ++ 11中删除一个函数

void foo(uint64_t) = delete;
Run Code Online (Sandbox Code Playgroud)

它通过在函数重载分辨率下添加签名来工作,如果它是更好的匹配,则会发生错误.您也可以使其通用,只允许您原始签名

template <class T> void foo( T&& ) = delete;
Run Code Online (Sandbox Code Playgroud)

  • 他问如何防止它发生,这将有效地防止它. (5认同)
  • 因为这是他要求的,如果有人向接收uint16的函数提供uint64,编译器会抛出错误 (3认同)
  • @JohnBollinger:给定的程序无法编译添加,不是吗? (2认同)

vso*_*tco 9

您还可以将其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;
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以拥有一个专门处理的重载uint16_t,以及另一个处理其他任何事情的重载.


Ale*_*evo 6

这是一个允许扩大转换并防止缩小转换的解决方案:

#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;
}
Run Code Online (Sandbox Code Playgroud)

如果您还想禁止带有签名类型参数的调用(有符号和无符号类型之间的转换不是"无损"),您可以使用以下声明:

#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;
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*igt 5

如果您想允许扩大转化次数,但禁止缩小转化次数,可能:

void foo(uint16_t x) {
}

template <class T>
void foo( const T&& t )
{
    return foo(uint16_t{t});
}
Run Code Online (Sandbox Code Playgroud)

这会强制除uint16_t自身之外的所有类型都经过列表初始化,这禁止缩小转换.

但是,如果你已经有很多重载,它就不能很好地工作.