事件
sizeof
返回size_t
类型,因此当作为参数传递给接受较窄类型(例如unsigned char
)的函数时,会发生隐式转换。在许多情况下,这些都是第三方库函数,因此它们的原型超出了我的控制范围。编译器现在通常足够聪明,可以检测这种转换是否真的会导致截断并警告您,但是一些静态代码分析器仍然会标记出这种情况,从而导致许多误报。显式转换sizeof
的结果通常会解决分析警告,但它会隐藏编译器警告,更不用说它会使事情变得笨拙。
template<class T1, class T2>
struct sizeofxx {
static constexpr T2 value{ sizeof(T1) };
};
template <class T>
constexpr unsigned int sizeof32 = sizeofxx<T, unsigned int>::value;
template <class T>
constexpr unsigned short sizeof16 = sizeofxx<T, unsigned short>::value;
template <class T>
constexpr unsigned char sizeof8 = sizeofxx<T, unsigned char>::value;
用法:
unsigned int foo = sizeof32<float>;
const char bar[255];
unsigned char foo3 = sizeof8<decltype(bar)>;
它依赖于聚合初始化来防止编译时的窄化转换。如果我使用了bar[256]
,构建失败。
但是正如您所看到的,在变量上使用它是相当笨拙的(由于需要decltype
)。有更简单的方法吗?我知道一种方法是将其封装在宏中,但这会阻止像Visual Studio这样的ide在您将鼠标悬停在它上面时帮助您解析值。另一种方法是创建一个constexpr
函数:
template <class T1>
constexpr unsigned char sizeof8f(T1&) {
return sizeof(T1);
}
但是这也不允许IDE代码时间分辨率,并且会扩展所涉及的符号数量,因为它们需要与早期对类型进行操作的实现具有不同的名称。
欢迎对解决根本问题(静态代码分析警告)提出任何其他建议。不,抑制它们是不可行的。
对于您的特定问题,即使在调试构建上也不需要任何运行时检查,因为该值本身就是constexpr
。您可以编写一个简单的实用程序,将值强制转换为能够保存该值的最小类型。
template<size_t N>
inline constexpr auto minuint = []{
if constexpr(N >= 1ull << 32)
return N;
else if constexpr(N >= 1ull << 16)
return uint32_t(N);
else if constexpr(N >= 1ull << 8)
return uint16_t(N);
else
return uint8_t(N);
}();
另一方面,没有函数或模板可以同时接受表达式和类型。模仿sizeof
行为的唯一可能方法是使用宏。
#define Sizeof(x) minuint<sizeof(x)>
有了这个,你就不会在缩小转换时得到错误的警告:如果有警告,你就做错了。
编译器很可能会优化出这个函数,即使没有godbolt上的constexpr
示例
你可能想使用T&&
或const T&
,所以它绑定到一个右值,取决于你想要什么。
template<typename size_type,typename T>
constexpr size_type sizeofxx(T&& t){
return sizeof(T);
}
这
template<typename size_type, typename object_type>
constexpr size_type
sizeof_as(object_type& object) {
// TODO: static_assert(...)
return size_type{sizeof object};
}
auto foo = ...; // some object
auto size = sizeof_as<uint8_t>(foo);
第一个模板形参显式给出,第二个由实参推导。
可以添加另一个不需要对象而只需要其类型的函数,类似于第二个sizeof
语法:
template<typename size_type, typename object_type>
constexpr size_type
sizeof_as() {
// TODO: static_assert(...)
return size_type{sizeof (object_type)};
}
Passer By的解决方案至少需要c++ 17才能工作,我刚刚想到c++ 11解决方案也是可能的。方法是使用SFINAE让编译器为每次调用选择适当的函数重载:
template<size_t S, std::enable_if_t<S <= 0xFFull, int> = 0>
constexpr auto mintype() { return (uint8_t)S; }
template<size_t S, std::enable_if_t<0xFFull < S && S <= 0xFFFFull, int> = 0>
constexpr auto mintype() { return (uint16_t)S; }
template<size_t S, std::enable_if_t<0xFFFFull < S && S <= 0xFFFFFFFFull, int> = 0>
constexpr auto mintype() { return (uint32_t)S; }
template<size_t S, std::enable_if_t<0xFFFFFFFFull < S, int> = 0>
constexpr auto mintype() { return S; }
#define sizeofex(S) mintype<sizeof(S)>()
仍然需要一个宏来方便使用。我们可以通过稍微调整实现来避免宏,代价是所需的代码翻倍:
// Overloads for variables
template<class T, std::enable_if_t<sizeof(T) <= 0xFFull, int> = 0>
constexpr uint8_t sizeofex(const T&) { return sizeof(T); }
template<class T, std::enable_if_t<0xFFull < sizeof(T) && sizeof(T) <= 0xFFFFull, int> = 0>
constexpr uint16_t sizeofex(const T&) { return sizeof(T); }
template<class T, std::enable_if_t<0xFFFFull < sizeof(T) && sizeof(T) <= 0xFFFFFFFFull, int> = 0>
constexpr uint32_t sizeofex(const T&) { return sizeof(T); }
template<class T, std::enable_if_t<0xFFFFFFFFull < sizeof(T), int> = 0>
constexpr uint64_t sizeofex(const T&) { return sizeof(T); }
// Overloads for types
template<class T, std::enable_if_t<sizeof(T) <= 0xFFull, int> = 0>
constexpr uint8_t sizeofex() { return sizeof(T); }
template<class T, std::enable_if_t<0xFFull < sizeof(T) && sizeof(T) <= 0xFFFFull, int> = 0>
constexpr uint16_t sizeofex() { return sizeof(T); }
template<class T, std::enable_if_t<0xFFFFull < sizeof(T) && sizeof(T) <= 0xFFFFFFFFull, int> = 0>
constexpr uint32_t sizeofex() { return sizeof(T); }
template<class T, std::enable_if_t<0xFFFFFFFFull < sizeof(T), int> = 0>
constexpr uint64_t sizeofex() { return sizeof(T); }
template<class T>
constexpr auto sizeoftype = sizeofex<T>(); // Optional, requires C++14
用法:
auto bar = sizeofex(var);
auto bar2 = sizeofex<type>();
auto bar3 = sizeoftype<type>; // Requires C++14
sizeoftype
的好处是,当你把鼠标放在它上面时,它可以被像Visual Studio这样的ide解析。