现在我使用以下代码将基本类型(int
,long
,char[]
,此类内容)dummily转换为std::string
以进行进一步处理:
template<class T>
constexpr std::string stringify(const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
但是,我不喜欢它取决于std::stringstream
的事实。我尝试使用std::to_string
(来自C ++ 11&#39; s保留曲目),但它会在char[]
个变量上窒息。
有没有一种简单的方法可以为这个问题提供优雅的解决方案?
答案 0 :(得分:10)
据我所知,这样做的唯一方法是通过SFINAE的参数类型对模板进行专门化。
您需要包含type_traits。
所以代替你的代码使用这样的东西:
template<class T>
typename std::enable_if<std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::to_string(t);
}
template<class T>
typename std::enable_if<!std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::string(t);
}
这个测试对我有用:
int main()
{
std::cout << stringify(3.0f);
std::cout << stringify("Asdf");
}
重要提示:传递给此函数的char数组需要以null结尾!
如yakk的评论中所述,您可以通过以下方式摆脱空终止:
template<size_t N> std::string stringify( char(const& s)[N] ) {
if (N && !s[N-1]) return {s, s+N-1};
else return {s, s+N};
}
答案 1 :(得分:9)
有没有一种简单的方法可以为这个问题提供优雅的解决方案?
由于没有人提出,请考虑使用boost::lexical_cast。
这与实现std :: ostream&lt;&lt;的任何内容无缝集成。运算符,可以扩展为自定义类型。
答案 2 :(得分:5)
我建议使用enable_if_t
,如果您要接受任何单个字符变量,那么您需要专注于这些变量:
template<typename T>
enable_if_t<is_arithmetic<T>::value, string> stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!is_arithmetic<T>::value, string> stringify(T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
这里我只是专攻char
。如果您需要专门化wchar
,char16
或char32
,那么您也需要这样做。
无论如何,对于非算术类型,这些重载将默认使用ostringstream
,这是一个很好的原因,如果你已经为你的一个类重载了它将处理它的提取操作符。
对于算术类型,这将使用to_string
,但char
和您重载的任何其他内容除外,这些可以直接创建string
。
修改强>
Dyp suggested使用to_string
是否接受T::type
的参数作为enable_if_t
条件。
只有is_detected
访问#include <experimental/type_traits>
时才能使用最简单的解决方案。如果你只是定义:
template<typename T>
using to_string_t = decltype(to_string(declval<T>()));
然后您可以将代码设置为:
template<typename T>
decltype(to_string(T{})) stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!experimental::is_detected<to_string_t, T>::value, string> (T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
我问this question弄清楚如何使用to_string
作为我的条件。如果您无法访问is_detected
,我强烈建议您阅读一些答案,因为它们非常现象:Metaprograming: Failure of Function Definition Defines a Separate Function
答案 3 :(得分:2)
虽然问题不在于 gimme the code 类,但由于我已经实现了解决方案,我想分享它:
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string;
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string;
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string;
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string;
inline auto buildString() -> std::string { return {}; }
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string {
return head + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string {
return std::to_string(head) + buildString(tail...);
}
用法:
auto gimmeTheString(std::string const &str) -> void {
cout << str << endl;
}
int main() {
std::string cpp_string{"This c++ string"};
char const c_string[] = "this c string";
gimmeTheString(buildString("I have some strings: ", cpp_string, " and ",
c_string, " and some number ", 24));
return 0;
}
答案 4 :(得分:2)
我相信,最优雅的解决方案是:
#include <string>
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
template <typename T>
typename std::enable_if<!std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
在这里,如果我们可以使用std::string
构建T
(我们在std::is_constructible<std::string, T>
的帮助下进行检查),那么我们会这样做,否则我们会使用to_string
。
当然,在C ++ 14中,您可以将typename std::enable_if<...>::type
替换为更短的std::enable_if_t<...>
。一个例子是在较短版本的代码中,就在下面。
以下是较短的版本,但效率稍低,因为它需要额外移动std::string
(但如果我们只做一个副本,那么效率会更低):
#include <string>
std::string stringify(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
template <typename T>
std::enable_if_t<!std::is_convertible<T, std::string>::value, std::string>
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
此版本使用隐式转换为std::string
,然后使用to_string
。请注意使用std::move
来利用C ++ 11 move semantics。
这就是为什么我的解决方案优于@cerkiewny目前投票最多的solution:
它有更广泛的适用性,因为,感谢ADL,它也是
为使用函数to_string
进行转换的任何类型定义
已定义(不仅是std::
版本),请参阅下面的示例用法。
而@cerkiewny的解决方案仅适用于基础
类型和std :: string可构造的类型。
当然,在他的情况下,可以添加额外的重载
stringify
适用于其他类型,但如果是这样的解决方案则不太可行
与添加to_string
的新ADL版本相比。而且机会是
高度,ADL兼容to_string
已在第三方库中定义
我们想要使用的类型。在这种情况下,使用我的代码,您无需编写任何其他代码即可使stringify
正常工作。
效率更高,
因为它利用了C ++ 11 perfect forwarding(通过使用通用引用(T&&
)和std::forward
)。
使用示例:
#include <string>
namespace Geom {
class Point {
public:
Point(int x, int y) : x(x), y(y) {}
// This function is ADL-compatible and not only 'stringify' can benefit from it.
friend std::string to_string(const Point& p) {
return '(' + std::to_string(p.x) + ", " + std::to_string(p.y) + ')';
}
private:
int x;
int y;
};
}
#include <iostream>
#include "stringify.h" // inclusion of the code located at the top of this answer
int main() {
double d = 1.2;
std::cout << stringify(d) << std::endl; // outputs "1.200000"
char s[] = "Hello, World!";
std::cout << stringify(s) << std::endl; // outputs "Hello, World!"
Geom::Point p(1, 2);
std::cout << stringify(p) << std::endl; // outputs "(1, 2)"
}
我还考虑过度重载to_string
:
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
to_string(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
使用隐式转换为std::string
的较短版本:
std::string to_string(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
但这些都有严重的局限性:我们需要记住在我们想要使用它的地方写to_string
而不是std::to_string
;它也与最常见的ADL使用模式不兼容:
int main() {
std::string a = std::to_string("Hello World!"); // error
using std::to_string; // ADL
std::string b = to_string("Hello World!"); // error
}
最可能的是,这种方法还存在其他问题。