Подтвердить что ты не робот

Запретить преобразование uint64_t в uint16_t

Почему следующий код компилируется в clang++?

Есть ли какие-либо флагов С++, чтобы это не происходило - я хотел бы, чтобы компилятор выдал ошибку, потому что я передаю 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;
}
4b9b3361

Ответ 1

вы можете удалить функцию в С++ 11

void foo(uint64_t) = delete;

он работает, добавляя подпись при разрешении перегрузки функции, и если это было лучшее совпадение, возникает ошибка. Вы также можете сделать его общим, чтобы разрешить только оригинальную подпись

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

Ответ 2

Вы также можете использовать 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, и другую перегрузку, которая имеет дело с чем-либо еще.

Ответ 3

Здесь решение, которое позволит расширить преобразования и предотвратить сужение:

#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;
}

Ответ 4

Если вы хотите разрешить расширение конверсий, но запретите сужение конверсий, возможно:

void foo(uint16_t x) {
}

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

Это заставляет все типы, кроме uint16_t самостоятельно проходить через инициализацию списка, что запрещает сужение конверсий.

Это не работает так хорошо, если у вас уже есть несколько перегрузок.

Ответ 5

Хотя большинство ответов здесь технически корректны, вы, скорее всего, не захотите, чтобы поведение применялось только к этой функции, поэтому решение "уровень кода", которое вы должны написать для каждого из этих случаев преобразования, вероятно, не то, что вы хотите.

На уровне "project/compilation" вы можете добавить этот флаг, чтобы предупредить вас об этих преобразованиях:

-Wconversion

или если вы предпочитаете прямо относиться к ним как к ошибкам:

-Werror=conversion