代码之家  ›  专栏  ›  技术社区  ›  tcb

扩展std::to_字符串以支持枚举和指针

c++
  •  0
  • tcb  · 技术社区  · 7 年前

    我有以下模板函数 ToString 使用 std::to_string 对于算术类型并尝试执行 static_cast 对于指针和枚举类型。

    #include <iostream>
    #include <string>
    
    template <typename T>
    std::string ToString(T val)
    {
        if (std::is_arithmetic<T>::value)
        {
            return std::to_string(val);
        }
    
        if (std::is_enum<T>::value || std::is_pointer<T>::value)
        {
            return std::to_string(static_cast<size_t>(val));
        }
    }
    
    enum class MyEnum : int
    {
        E1,
        E2
    };
    
    int main(int argc, char* argv[])
    {
        MyEnum e = MyEnum::E1;
        const void* ptr = &e;
    
        std::cout << ToString(e) << std::endl;
        std::cout << ToString(ptr) << std::endl;
    }
    

    上面的代码无法编译。有没有方法可以实现所需的功能?

    VS2017的编译错误是

    Error   C2440   'static_cast': cannot convert from 'T' to 'size_t'
    Error   C2665   'std::to_string': none of the 9 overloads could convert all the argument types
    
    1 回复  |  直到 7 年前
        1
  •  4
  •   eerorika    7 年前

    你需要使用 if constexpr . 否则你将实例化 std::to_string 指针或枚举,无法编译(C2665)。

    此外,您不能将指针静态强制转换为整数(C2440)。你需要重新解释演员阵容。

    此外,如果传递的参数不是枚举、指针或算术类型,则函数缺少返回。在这种情况下,行为是不确定的。解决方案:总是返回某些内容(如果模板参数无效,则编译失败)。

    也, size_t 不能保证足够大以表示所有指针值。你想要的 std::uintptr_t .

    你可能会想用 std::underlying_type_t 为了得到正确的类型 enum class .

    if constexpr (std::is_pointer<T>::value) {
        return std::to_string(reinterpret_cast<std::uintptr_t>(val));
    } else if constexpr (std::is_enum<T>::value)
    {
        return std::to_string(static_cast<std::underlying_type_t<T>>(val));
    } else {
        return std::to_string(val);
    }
    

    一旦添加了丢失的标题,就应该可以了。

    P、 注意:现在打印指针、整数或枚举的输出都是相同的。您可能需要添加一些前缀或类似的前缀来消除结果的歧义。

        2
  •  1
  •   templatetypedef    7 年前

    我认为现在的问题是 static_cast 如果T是指针类型。要将指针转换为整数,需要使用 reinterpret_cast 而不是 静态铸造 ,因为该转换基本上将指针变量的位重新解释为数字,而不是简单地告诉C++如果想被指的是什么类型的对象。

    正如注释所指出的,您可能需要对代码进行一些重构以使其正确工作,因为当您实例化模板时,函数体中的所有代码将被编译为T的选择,以添加到如何执行此操作的建议列表中,如果您使用C++ 17编译器,请考虑使用 if constexpr 在你的代码中而不是常规的 if . 这将告诉C++只条件编译IF/EL链的不同分支。

        3
  •  1
  •   wreckgar23    7 年前

    这适用于c++11。我把它拼凑在一起,所以我想我还是把它贴出来吧。不过,你应该注意这里的其他答案。

    #include <cstdint>                                                                                                                                                                                                   
    #include <string>                                                                                                                                                                                                    
    #include <type_traits>                                                                                                                                                                                               
    #include <iostream>                                                                                                                                                                                                  
    
    template <typename T>                                                                                                                                                                                                
    auto ToString(T val) -> typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type                                                                                                                     
    {                                                                                                                                                                                                                    
            return std::to_string(val);                                                                                                                                                                                  
    }                                                                                                                                                                                                                    
    
    template <typename T>                                                                                                                                                                                                
    auto ToString(T val) -> typename std::enable_if<std::is_enum<T>::value, std::string>::type                                                                                                                           
    {                                                                                                                                                                                                                    
            return std::to_string(static_cast<typename std::underlying_type<T>::type>(val));                                                                                                                                                             
    }                                                                                                                                                                                                                    
    
    
    template <typename T>                                                                                                                                                                                                
    auto ToString(T val) -> typename std::enable_if<std::is_pointer<T>::value, std::string>::type                                                                                                                        
    {                                                                                                                                                                                                                    
            return std::to_string(reinterpret_cast<std::uintptr_t>(val));                                                                                                                                                        
    }                                                                                                                                                                                                                    
    
    enum class MyEnum : int                                                                                                                                                                                              
    {                                                                                                                                                                                                                    
            E1,                                                                                                                                                                                                          
            E2                                                                                                                                                                                                           
    };                                                                                                                                                                                                                   
    
    int main(int argc, char* argv[])                                                                                                                                                                                     
    {                                                                                                                                                                                                                    
            MyEnum e = MyEnum::E1;                                                                                                                                                                                       
            const void* ptr = &e;                                                                                                                                                                                        
    
            std::cout << ToString(e) << std::endl;                                                                                                                                                                       
            std::cout << ToString(ptr) << std::endl;                                                                                                                                                                     
    }