如何检测参数包中的uint8_t类型



注意:以下代码是根据这篇文章修改的:https://stackoverflow.com/a/27375389

#include <iostream>
#include <type_traits>
template<class Head>
void print_args(std::ostream& s, Head&& head) {
s << std::forward<Head>(head);
}
template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
if (std::is_same<Head, uint8_t>::value)
s << static_cast<int>(head) << ",";       // cast uint8_t so that the value of 1 or 0 can be displayed correctly in console
else
s << std::forward<Head>(head) << ",";
print_args(s, std::forward<Tail>(tail)...);
}
template<class... Args>
void print_args(Args&&... args) {
print_args(std::cout, std::forward<Args>(args)...);
}
int main()
{
uint8_t val = 1;
print_args(std::string("hello"), val); // error: invalid static_cast from type 'std::basic_string<char>' to type 'int'
print_args("hello", val); // error: invalid static_cast from type 'const char [6]' to type 'int'
}

问题>我需要将uint_8转换为int,以便可以在控制台中正确显示该值。但是,上面的代码对于std::stringconst char*都有构建问题。

该函数的修复方法是什么?

if在运行时进行评估,因此编译器必须为其编译两个分支,并且static_cast<int>(head)无法针对非算术类型进行编译(例如std::string(。

在 C++17 中,您可以使用if constexpr(std::is_same<Head, uint8_t>::value).

在 C++17 之前,添加一个额外的函数,您可以重载uint8_t

template<class T>
auto convert(T&& t) -> decltype(std::forward<T>(t)) {
return std::forward<T>(t);
}
int convert(uint8_t t) {
return t;
}
template<class Head>
void print_args(std::ostream& s, Head&& head) {
s << convert(std::forward<Head>(head));
}
template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
s << convert(std::forward<Head>(head)) << ',';
print_args(s, std::forward<Tail>(tail)...);
}

你有几个问题。 对于编译时问题:

if (std::is_same<Head, uint8_t>::value)

对此:

if constexpr (std::is_same<Head, uint8_t>::value)

这是因为您只想在 if 有效转换为 uint 时编译它的主体。 仅仅因为运行时 if 为 false 并不意味着其中的代码不一定有效。 如果可以解决这个问题。

接下来,您的类型比较过于严格。 将引用与非引用进行比较将返回 false。 因此,您希望在is_same测试之前衰减类型。 我也更喜欢这里的is_same_v:

if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)

最后,你的基本情况,最后一个元素,仍然需要相同类型的"if constexpr"来正确打印它,但你只在主循环中应用了这个逻辑。

把它放在一起:

template<class Head>
void print_args(std::ostream& s, Head&& head) {
if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)
s << static_cast<int>(head);
else
s << head << ",";}
template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)
s << static_cast<int>(head) << ",";
else
s << head << ",";
print_args(s, std::forward<Tail>(tail)...);
}
template<class... Args>
void print_args(Args&&... args) {
print_args(std::cout, std::forward<Args>(args)...);
}

您可以将常见的 if constexpr 分解到单个辅助函数中,以减少冗余代码,使其保持干燥。 但与其去那里,不如提出别的建议。

只是让它工作并不总是最好的目标。 递归模板可能会在编译器中使用更多内存并减慢生成速度,因此避免递归的解决方案如果产生相同的结果会更好。

因此,请考虑一个折叠表达式 (c++17( 和一个 lambda 来打印代码:

以上所有内容都可以替换为以下内容

template<class... Args>
void print_args(Args&&... args) {
([](auto&& arg)
{
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, uint8_t>)
std::cout << static_cast<int>(std::forward<Args>(arg));
else
std::cout << arg;
if (sizeof...(Args) > 1)
std::cout << ",";
}(args), ...);
}

分解一下,它在逗号运算符上使用折叠表达式,使用形式为 (f(x( op ...( 的 IILE(立即调用的 Lambda 表达式(,其中 f(x( 是 lambda(给定当前参数(,"op"是逗号运算符,用于对调用进行排序。 第二个"if"可防止尾随逗号。

一种可能的解决方案:

#include <iostream>
#include <type_traits>
namespace detail
{
namespace cpp11
{
template<class arg>
void print(std::ostream& s, arg&& a, typename std::enable_if<std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>::type* =0)
{
s << static_cast<int>(a) << ",";
}
template<class arg>
void print(std::ostream& s, arg&& a, typename std::enable_if<!std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>::type* =0)
{
s << std::forward<arg>(a) << ",";
}
}
namespace cpp14
{
template<class arg>
void print(std::ostream& s, arg&& a, std::enable_if_t<std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>* =0)
{
s << static_cast<int>(a) << ",";
}
template<class arg>
void print(std::ostream& s, arg&& a, std::enable_if_t<!std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>* =0)
{
s << std::forward<arg>(a) << ",";
}
}
namespace cpp17
{
template<class arg>
void print(std::ostream& s, arg&& a)
{
if constexpr (std::is_same_v<std::remove_reference_t<std::remove_cv_t<arg>>, uint8_t>)
s << static_cast<int>(a) << ",";
else
s << std::forward<arg>(a) << ",";
}
}
namespace cpp20
{
template<class arg>
void print(std::ostream& s, arg&& a)
{
if constexpr (std::is_same_v<std::remove_cvref_t<arg>, uint8_t>)
s << static_cast<int>(a) << ",";
else
s << std::forward<arg>(a) << ",";
}
}
template<class Head>
void print_args(std::ostream& s, Head&& head)
{
//cpp11::print(s, std::forward<Head>(head));
//cpp14::print(s, std::forward<Head>(head));
//cpp17::print(s, std::forward<Head>(head));
cpp20::print(s, std::forward<Head>(head));
}
template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail)
{
print_args(s, std::forward<Head>(head));
print_args(s, std::forward<Tail>(tail)...);
}
}
template<class... Args>
void print_args(Args&&... args)
{
detail::print_args(std::cout, std::forward<Args>(args)...);
}
int main()
{
uint8_t val = 1;
print_args(std::string("hello"), val);
print_args("hello", val);
}

一些额外的说明:

  • 它只是检查uint8_t,但您可能也会遇到其他 POD 类型的问题,例如int8_tbyte等。
  • 通常,这些类似打印的函数应通过 ref: 返回std::ostream参数template<...> std::ostream& print(...) {...; return s;}
  • 您可以通过std::remove_cv{_t}std::remove_reference{_t}轻松实现std::remove_cvref{_t}

相关内容

最新更新