在 VC++ 2010 中,std::to_string 的三个重载分别采用 long long、unsigned long long 和 long double — 显然 int 不是这些,而且没有一种转换比另一种更好(@ 987654321@),因此不能隐式/明确地进行转换。
就真正的 C++11 支持而言,这是 VC++ 2010 标准库实现方面的一个失败——C++11 标准本身实际上调用了std::to_string 的九个 重载([string.conversions]/7):
string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);
如果所有这些重载都存在,您显然不会遇到这个问题;但是,VC++ 2010 并不是基于实际的 C++11 标准(在其发布时还不存在),而是基于 N3000(来自 2009),它确实不 调用这些额外的重载。因此,在这里责备 VC++ 太多是很苛刻的......
无论如何,对于少数几个调用,使用强制转换自己解决歧义并没有错:
void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
name += std::to_string(static_cast<long long>(counter));
}
或者,如果您的代码库中大量使用 std::to_string,请编写一些包装器并使用它们来代替 - 这样,就不需要调用站点强制转换:
#include <type_traits>
#include <string>
template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, std::string>::type
to_string(T const val) {
return std::to_string(static_cast<long long>(val));
}
template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, std::string>::type
to_string(T const val) {
return std::to_string(static_cast<unsigned long long>(val));
}
template<typename T>
inline typename std::enable_if<std::is_floating_point<T>::value, std::string>::type
to_string(T const val) {
return std::to_string(static_cast<long double>(val));
}
// ...
void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
name += to_string(counter);
}
我无法通过上述 SFINAE 用法检查 VC++ 2010 是成功还是失败;如果失败,以下内容——使用标签调度而不是 SFINAE——应该是可编译的(如果可能不太清楚):
#include <type_traits>
#include <string>
namespace detail {
template<typename T> // is_float is_unsigned
inline std::string to_string(T const val, std::false_type, std::false_type) {
return std::to_string(static_cast<long long>(val));
}
template<typename T> // is_float is_unsigned
inline std::string to_string(T const val, std::false_type, std::true_type) {
return std::to_string(static_cast<unsigned long long>(val));
}
template<typename T, typename _> // is_float
inline std::string to_string(T const val, std::true_type, _) {
return std::to_string(static_cast<long double>(val));
}
}
template<typename T>
inline std::string to_string(T const val) {
return detail::to_string(val, std::is_floating_point<T>(), std::is_unsigned<T>());
}