扩展 std::to_string 以支持枚举和指针

Extending std::to_string to support enums and pointers

本文关键字:支持 枚举 指针 string std to 扩展      更新时间:2023-10-16

我有以下模板函数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

你需要使用 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);
}

添加缺少的标头后,这应该可以工作。

附言设计说明:现在打印指针,整数或枚举在输出中都是相同的。您可能希望添加一些前缀或类似前缀以消除结果的歧义。

我认为这里的直接问题是 T 是否为指针类型的static_cast。要将指针转换为整数,您需要使用reinterpret_cast而不是static_cast,因为该强制转换从根本上将指针变量的位重新解释为数字,而不是简单地告诉C++如果认为指向的对象,则更改类型对象。

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

适用于 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;                                                                                                                                                                     
}