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

问题描述 投票:1回答:3

注意:以下代码是根据以下帖子修改的: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*构建了问题。

该功能的修复是什么?

c++ c++11
3个回答
5
投票

在运行时评估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)...);
}

1
投票

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

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

对此:

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

那是因为你只想编译if的主体,当它转换为uint时是有效的。仅仅因为运行时if为false并不意味着其中的代码不必有效。 constexpr如果能解决这个问题。

接下来,您的类型比较过于严格。比较对非引用的引用将返回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), ...);
}

打破它,它使用逗号运算符上的fold表达式,使用形式(f(x)op ...)的IILE(立即调用的Lambda表达式),其中f(x)是lambda(给定当前arg) ,“op”是用于对调用进行排序的逗号运算符。)第二个“if”用于阻止尾随逗号。


0
投票

一种可能的方案:

#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_cvref{_t}std::remove_cv{_t}轻松实现std::remove_reference{_t}
© www.soinside.com 2019 - 2024. All rights reserved.