Ant*_*nio 14 c++ templates suppress-warnings
是否可以创建一个模板化函数来检查原始数据类型是否适合可能不同的原始数据类型的值?我们暂时将范围限制为整数类型.
更确切地说:是否有可能创建一个"一个适合所有"的模板化函数,而不会得到编译器警告(布尔表达式总是真/假,有符号/无符号比较,未使用的变量)并且不禁用编译器警告检查?这些函数还应该在运行时尽可能地限制检查(在编译时应排除所有琐碎的情况).如果可能的话,我宁愿避免使用C++ 11之类的扩展(除非存在"旧"C++的"快速"替代).
注意:"value"在编译时是未知的,只是其类型.
预期行为的示例:
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
const int value = atoi(argv[i]);
std::cout << value << ": ";
std::cout << CanTypeFitValue<int8_t>(value) << " ";
std::cout << CanTypeFitValue<uint8_t>(value) << " ";
std::cout << CanTypeFitValue<int16_t>(value) << " ";
std::cout << CanTypeFitValue<uint16_t>(value) << " ";
std::cout << CanTypeFitValue<int32_t>(value) << " ";
std::cout << CanTypeFitValue<uint32_t>(value) << " ";
std::cout << CanTypeFitValue<int64_t>(value) << " ";
std::cout << CanTypeFitValue<uint64_t>(value) << std::endl;
}
}
./a.out 6 1203032847 2394857 -13423 9324 -192992929
6: 1 1 1 1 1 1 1 1
1203032847: 0 0 0 0 1 1 1 1
2394857: 0 0 0 0 1 1 1 1
-13423: 0 0 1 0 1 0 1 0
9324: 0 0 1 1 1 1 1 1
-192992929: 0 0 0 0 1 0 1 0
Run Code Online (Sandbox Code Playgroud)
检查此处生成的程序集.
这个问题的灵感来自这篇文章
使用stdint.h中定义的numeric_limits和类型
更紧凑,我的第一个解决方案,相同的效率.
缺点:要包含一个额外的标题.
#include <limits>
#include <stdint.h>
using std::numeric_limits;
template <typename T, typename U>
bool CanTypeFitValue(const U value) {
const intmax_t botT = intmax_t(numeric_limits<T>::min() );
const intmax_t botU = intmax_t(numeric_limits<U>::min() );
const uintmax_t topT = uintmax_t(numeric_limits<T>::max() );
const uintmax_t topU = uintmax_t(numeric_limits<U>::max() );
return !( (botT > botU && value < static_cast<U> (botT)) || (topT < topU && value > static_cast<U> (topT)) );
}
Run Code Online (Sandbox Code Playgroud)
生成汇编代码(可以更改T和U类型)
使用C++11(是的,我知道你没有要求它,但它很有趣)和使用模板的功能,这就是我提出的:
http://ideone.com/lBxnAW(更新版本:现在也接受unsigned签名,简短而美观)
这基本上std::enable_if广泛用于type_traits std::is_unsigned和std::is_integral.最好从下往上阅读(因为决策树从那里建立起来).
显然这几乎都是在编译时完成的,所以汇编应该相当小.
该解决方案可以处理整数和浮点目标类型以及整数和浮点原始类型.
如果检查是不平凡的(即数据类型的边界必须检查)时,actual_type值n被浇铸到typename std::common_type<target, actual_type>::type静态.
每一个决定is_integral,并is_unsigned和is_same在编译时完成的,所以从这个在运行时的开销.检查归结为一些lower_bound(target) <= value和/或value <= upper_bound(target)在将类型转换为普通类型之后(以避免警告并防止溢出).
#include <cmath> // necessary to check for floats too
#include <cstdint> // for testing only
#include <iomanip> // for testing only
#include <iostream> // for testing only
#include <limits> // necessary to check ranges
#include <type_traits> // necessary to check type properties (very efficient, compile time!)
// the upper bound must always be checked
template <typename target_type, typename actual_type>
bool test_upper_bound(const actual_type n)
{
typedef typename std::common_type<target_type, actual_type>::type common_type;
const auto c_n = static_cast<common_type>(n);
const auto t_max = static_cast<common_type>(std::numeric_limits<target_type>::max());
return ( c_n <= t_max );
}
// the lower bound is only needed to be checked explicitely in non-trivial cases, see the next to functions
template <typename target_type, typename actual_type>
typename std::enable_if<!(std::is_unsigned<target_type>::value), bool>::type
test_lower_bound(const actual_type n)
{
typedef typename std::common_type<target_type, actual_type>::type common_type;
const auto c_n = static_cast<common_type>(n);
const auto t_min = static_cast<common_type>(std::numeric_limits<target_type>::lowest());
return ( c_n >= t_min );
}
// for unsigned target types, the sign of n musn't be negative
// but that's not an issue with unsigned actual_type
template <typename target_type, typename actual_type>
typename std::enable_if<std::is_integral<target_type>::value &&
std::is_unsigned<target_type>::value &&
std::is_integral<actual_type>::value &&
std::is_unsigned<actual_type>::value, bool>::type
test_lower_bound(const actual_type)
{
return true;
}
// for unsigned target types, the sign of n musn't be negative
template <typename target_type, typename actual_type>
typename std::enable_if<std::is_integral<target_type>::value &&
std::is_unsigned<target_type>::value &&
(!std::is_integral<actual_type>::value ||
!std::is_unsigned<actual_type>::value), bool>::type
test_lower_bound(const actual_type n)
{
return ( n >= 0 );
}
// value may be integral if the target type is non-integral
template <typename target_type, typename actual_type>
typename std::enable_if<!std::is_integral<target_type>::value, bool>::type
test_integrality(const actual_type)
{
return true;
}
// value must be integral if the target type is integral
template <typename target_type, typename actual_type>
typename std::enable_if<std::is_integral<target_type>::value, bool>::type
test_integrality(const actual_type n)
{
return ( (std::abs(n - std::floor(n)) < 1e-8) || (std::abs(n - std::ceil(n)) < 1e-8) );
}
// perform check only if non-trivial
template <typename target_type, typename actual_type>
typename std::enable_if<!std::is_same<target_type, actual_type>::value, bool>::type
CanTypeFitValue(const actual_type n)
{
return test_upper_bound<target_type>(n) &&
test_lower_bound<target_type>(n) &&
test_integrality<target_type>(n);
}
// trivial case: actual_type == target_type
template <typename actual_type>
bool CanTypeFitValue(const actual_type)
{
return true;
}
int main()
{
int ns[] = {6, 1203032847, 2394857, -13423, 9324, -192992929};
for ( const auto n : ns )
{
std::cout << std::setw(10) << n << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(n);
std::cout << " " << CanTypeFitValue<uint8_t>(n);
std::cout << " " << CanTypeFitValue<int16_t>(n);
std::cout << " " << CanTypeFitValue<uint16_t>(n);
std::cout << " " << CanTypeFitValue<int32_t>(n);
std::cout << " " << CanTypeFitValue<uint32_t>(n);
std::cout << " " << CanTypeFitValue<int64_t>(n);
std::cout << " " << CanTypeFitValue<uint64_t>(n);
std::cout << " " << CanTypeFitValue<float>(n);
std::cout << " " << CanTypeFitValue<double>(n);
std::cout << "\n";
}
std::cout << "\n";
unsigned long long uss[] = {6, 1201146189143ull, 2397, 23};
for ( const auto n : uss )
{
std::cout << std::setw(10) << n << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(n);
std::cout << " " << CanTypeFitValue<uint8_t>(n);
std::cout << " " << CanTypeFitValue<int16_t>(n);
std::cout << " " << CanTypeFitValue<uint16_t>(n);
std::cout << " " << CanTypeFitValue<int32_t>(n);
std::cout << " " << CanTypeFitValue<uint32_t>(n);
std::cout << " " << CanTypeFitValue<int64_t>(n);
std::cout << " " << CanTypeFitValue<uint64_t>(n);
std::cout << " " << CanTypeFitValue<float>(n);
std::cout << " " << CanTypeFitValue<double>(n);
std::cout << "\n";
}
std::cout << "\n";
float fs[] = {0.0, 0.5, -0.5, 1.0, -1.0, 1e10, -1e10};
for ( const auto f : fs )
{
std::cout << std::setw(10) << f << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(f);
std::cout << " " << CanTypeFitValue<uint8_t>(f);
std::cout << " " << CanTypeFitValue<int16_t>(f);
std::cout << " " << CanTypeFitValue<uint16_t>(f);
std::cout << " " << CanTypeFitValue<int32_t>(f);
std::cout << " " << CanTypeFitValue<uint32_t>(f);
std::cout << " " << CanTypeFitValue<int64_t>(f);
std::cout << " " << CanTypeFitValue<uint64_t>(f);
std::cout << " " << CanTypeFitValue<float>(f);
std::cout << " " << CanTypeFitValue<double>(f);
std::cout << "\n";
}
}
Run Code Online (Sandbox Code Playgroud)
这个(新)版本快速决定(在编译时!)是否需要检查(关于上限,下限和完整性)并使用正确的版本(以避免关于愚蠢的> = 0与无符号类型的比较的警告)(也在编译时)时间).例如,如果目标是浮点数,则不需要检查完整性,如果两个类型都是无符号的,则不需要检查下限.
最明显的优化(具有相同的类型),完成std::is_same.
此方法还可以扩展为使用专用模板的已定义类型.std::is_integral对这些类型的检查将是否定的.
您可以在此处检查汇编程序输出是否相当小(除了明显的浮点数),或者通过使用-S调用g ++.
当然
template <typename T, typename U>
constexpr bool CanTypeFitValue(const U value)
{return ((value>U(0))==(T(value)>T(0))) && U(T(value))==value;}
// ( part1 ) && ( part2 )
Run Code Online (Sandbox Code Playgroud)
基本上,这有两个部分.第一部分确认如果发生符号更改(转换unsigned为signed反之亦然,则符号信息不会丢失.第二部分只是检查是否转换value为a T和back,它保留了它的值,并且没有位迷路了.
仅供参考我不确定这是否足以判断价值是否得到维持,但不能立即想到原型会失败的情况.我的答案和Casey的答案都应该适用于用户定义的类数字类型,只要它们在T和之间提供转换运算符U.