禁止精度损失的整数转换

如何防止编译这些代码?

#include <vector>
#include <limits>
#include <iostream>
#include <cstdint>

int main() {
  std::vector<int16_t> v;
  v.emplace_back(std::numeric_limits<uint64_t>::max());
  std::cout << v.back() << std::endl;
  return 0;
}

g ++和clang与-std=c++14 -Wall -Wextra -Werror -pedantic -Wold-style-cast -Wconversion -Wsign-conversion甚至不会提醒它。 这个例子也用std::vector<uint16_t>编译时没有警告


在命令行中添加-Wsystem-headers 。 在许多虚假的警告中,您会发现所需的警告。

In file included from (...)include/c++/6.3.0/x86_64-w64-mingw32/bits/c++allocator.h:33:0,
                 from (...)include/c++/6.3.0/bits/allocator.h:46,
                 from (...)include/c++/6.3.0/vector:61,
                 from test.cpp:1:
(...)include/c++/6.3.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = short int; _Args = {long long unsigned int}; _Tp = short int]':
(...)include/c++/6.3.0/bits/alloc_traits.h:455:4:   required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = short int; _Args = {long long unsigned int}; _Tp = short int; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<short int>]'
(...)include/c++/6.3.0/bits/vector.tcc:96:30:   required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {long long unsigned int}; _Tp = short int; _Alloc = std::allocator<short int>]'
test.cpp:9:54:   required from here
(...)include/c++/6.3.0/ext/new_allocator.h:120:4: error: conversion to 'short int' from 'long long unsigned int' may alter its value [-Werror=conversion]
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^

我知道这不是一个真正的解决方案,尽管它在技术上回答了这个问题。

问题是, emplace_back将所有参数(本例中为uint64_t转发给包含类型的构造函数。 首先, emplace_back的参数被推导为uint64_t 。 在安置回来时不发生转换。 然后缩小的转换在系统头文件中的emplace_back实现内“发生”。 编译器不知道这是调用者的错误,并且因为它位于系统头部中而禁止该警告。


我通过模板和专业化来解决这个问题:

 template<
        typename T/*the desired type*/,
        typename Y/*the source type*/
    > T integral_cast(const Y& y)
    {
        static_assert(false, "undefined integral_cast");
    }

如果我希望演员能够工作,那么我就会专注于休闲娱乐:

// Pass through for uint32_t
    template<>
    inline std::uint32_t integral_cast(const uint32_t& y)
    {
        return y;
    }

// Specialisation to convert std::uint32_t to double
    template<>
    inline double integral_cast(const std::uint32_t& y)
    {
        double ret = static_cast<double>(y); // this never loses precision under IEEE754
        return ret;
    }

在使用的地方,您可以编写表单的代码

int16_t y = integral_cast<int16_t>(std::numeric_limits<uint64_t>::max());

你可以开始为你的整数类型编写一个包装来匹配确切的类型(或者某些条件)。

#include <cstdint>
#include <iostream>
#include <limits>
#include <type_traits>
#include <vector>

template <class...> struct conjunction : std::true_type {};
template <class B1> struct conjunction<B1> : B1 {};
template <class B1, class... Bn>
struct conjunction<B1, Bn...>
    : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};

template <typename T> struct int_wrapper {
  explicit int_wrapper() : _val{T{}} {}

  explicit int_wrapper(const int_wrapper &other) : _val{other._val} {}

  template <typename U> explicit int_wrapper(U val) : _val{val} {
    static_assert(sizeof(T) >= sizeof(U), "Size mismatch.");
    static_assert(conjunction<std::is_signed<T>, std::is_signed<U>>::value,
                  "sign mismatch");
  }

  explicit operator T() { return _val; }
  explicit operator T() const { return _val; }

  T _val;
};

std::ostream &operator<<(std::ostream &stream, const int_wrapper<int16_t> &v) {
  stream << v._val;
  return stream;
}

int main() {
  std::vector<int_wrapper<int16_t>> v;
  v.emplace_back(std::numeric_limits<uint64_t>::max());
  std::cout << v.back() << std::endl;

  return 0;
}

在macOS上的铛声给你错误,如:

so.cpp:18:60: error: non-constant-expression cannot be narrowed from type 'unsigned long long' to 'short' in initializer list
      [-Wc++11-narrowing]
  template <typename U> explicit int_wrapper(U val) : _val{val} {
                                                           ^~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1752:31: note: in
      instantiation of function template specialization 'int_wrapper<short>::int_wrapper<unsigned long long>' requested here
            ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1668:18: note: in
      instantiation of function template specialization 'std::__1::allocator<int_wrapper<short> >::construct<int_wrapper<short>,
      unsigned long long>' requested here
            {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                 ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1514:14: note: in
      instantiation of function template specialization 'std::__1::allocator_traits<std::__1::allocator<int_wrapper<short> >
      >::__construct<int_wrapper<short>, unsigned long long>' requested here
            {__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:1643:25: note: in
      instantiation of function template specialization 'std::__1::allocator_traits<std::__1::allocator<int_wrapper<short> >
      >::construct<int_wrapper<short>, unsigned long long>' requested here
        __alloc_traits::construct(this->__alloc(),
                        ^
so.cpp:37:5: note: in instantiation of function template specialization 'std::__1::vector<int_wrapper<short>,
      std::__1::allocator<int_wrapper<short> > >::emplace_back<unsigned long long>' requested here
  v.emplace_back(std::numeric_limits<uint64_t>::max());
    ^
so.cpp:18:60: note: insert an explicit cast to silence this issue
  template <typename U> explicit int_wrapper(U val) : _val{val} {
                                                           ^~~
                                                           static_cast<short>( )
so.cpp:19:5: error: static_assert failed "Not the same size."
    static_assert(sizeof(T) >= sizeof(U), "Not the same size.");
    ^             ~~~~~~~~~~~~~~~~~~~~~~
so.cpp:20:5: error: static_assert failed "sign mismatch"
    static_assert(conjunction<std::is_signed<T>, std::is_signed<U>>::value,
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
链接地址: http://www.djcxy.com/p/39947.html

上一篇: Forbid integer conversion with precision loss

下一篇: Jenkins edit config in multi