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

时间:2018-08-31 13:59:02

标签: c++ c++11

注意:以下代码是根据以下信息进行修改的: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*上均存在构建问题。

该功能的解决方法是什么?

3 个答案:

答案 0 :(得分:5)

.dropdown-menu { display : none !important; } .dropdown-menu.show { display : block !important; } 是在运行时求值的,因此编译器必须为其编译两个分支,而if不能针对非算术类型(例如static_cast<int>(head))进行编译。

在C ++ 17中,您可以使用std::string进行修复。

在C ++ 17之前,添加一个可以为if constexpr(std::is_same<Head, uint8_t>::value)重载的额外功能:

uint8_t

答案 1 :(得分:1)

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

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

对此:

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

那是因为您只希望在if强制转换为uint时编译if的主体。仅因为运行时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)...);
}

您可以将constexpr的公共属性分解为单个辅助函数,以减少冗余代码,使其保持DRY。但是,除了去那里,我会提出其他建议。

仅使其工作并不总是最好的目标。递归模板可以在编译器中使用更多的内存,并降低构建速度,因此,如果避免产生递归的结果相同,则避免递归的解决方案会更好。

这样,考虑一个fold表达式(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”防止尾部逗号。

答案 2 :(得分: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等,您也可能会遇到问题。
  • 通常,这些类似于打印的函数应通过引用std::ostream返回template<...> std::ostream& print(...) {...; return s;}参数
  • 您可以使用std::remove_cvref{_t}std::remove_cv{_t}轻松实现std::remove_reference{_t}