C++ 使用户定义的类std::to_可字符串化
我知道这似乎太多Java或C语言了。但是,是否可以/很好/明智地使我自己的类作为函数C++ 使用户定义的类std::to_可字符串化,c++,string,c++11,tostring,C++,String,C++11,Tostring,我知道这似乎太多Java或C语言了。但是,是否可以/很好/明智地使我自己的类作为函数std::to_string的输入有效? 例如: class my\u class{ 公众: std::string给我一个字符串{ 返回“我是”+std::to_字符串(I); } int i; }; void main(){ 我的类我的对象; std::cout您可能只想重载操作符您可以在自己的名称空间(例如,foo)中定义自己的to_string 并将其用作: int main(){ my_clas
std::to_string
的输入有效?
例如:
class my\u class{
公众:
std::string给我一个字符串{
返回“我是”+std::to_字符串(I);
}
int i;
};
void main(){
我的类我的对象;
std::cout您可能只想重载操作符您可以在自己的名称空间(例如,foo
)中定义自己的to_string
并将其用作:
int main(){
my_class my_object;
std::cout<< foo::to_string(my_object);
}
intmain(){
我的类我的对象;
std::cout什么是“最佳”方式是一个悬而未决的问题
有几种方法
首先要说的是,不允许为自定义类型重载std::to_string
。我们可能只在std
命名空间中专门为自定义类型指定模板函数和类,而std::to_string
不是模板函数
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
这就是说,处理到_string
的好方法很像一个操作符或交换的一个实现。也就是说,允许依赖于参数的查找来完成这项工作
因此,当我们想将某些内容转换为字符串时,我们可以编写:
using std::to_string;
auto s = to_string(x) + " : " + to_string(i);
假设x是命名空间Y中x类型的对象,而我是int,那么我们可以定义:
namespace Y {
std::string to_string(const X& x);
}
这意味着:
调用to_string(x)
实际上会选择Y::to_string(const Y::x&)
,并且
调用到字符串(i)
选择std::到字符串(int)
更进一步说,您可能希望\u string执行与运算符相同的操作。您不能将的新重载添加到\u string
到std
命名空间中,但您可以在命名空间中执行此操作:
namespace my {
using std::to_string;
std::string to_string(const my_class& o) {
return o.give_me_a_string_of_you();
}
}
然后可以对所有类型使用my::to_string
int main()
{
my_class my_object;
std::cout << my::to_string(my_object);
std::cout << my::to_string(5);
}
intmain()
{
我的类我的对象;
std::cout首先,一些ADL帮助:
namespace notstd {
namespace adl_helper {
template<class T>
std::string as_string( T&& t ) {
using std::to_string;
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
return adl_helper::as_string(std::forward<T>(t));
}
}
现在notstd::to_string(我的\u对象)
找到正确的to_string
,就像notstd::to_string(7)
一样
通过更多的工作,我们甚至可以在自动检测和使用的类型上支持.tostring()
方法
这里有一个替代解决方案。没有什么比这更优雅或太花哨的了,但它是一种替代方法。它假设所有要调用的类都有一个ToString()函数
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
这是一个函数模板,它只处理类类型的对象,并调用ToString()函数
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
模板
标准::字符串到_字符串(常量T&T){
返回t.ToString();
}
也许我们也希望它与std::string一起工作
template<>
std::string to_string(const std::string& t) {
return t;
}
模板
std::string到_string(const std::string&t){
返回t;
}
这是一个正在使用的代码示例。请注意虚拟名称空间to_s。我想如果您在主函数中使用std::to_字符串,它会影响我们的模板函数名称,因此我们必须像这样间接地引入名称。如果有人知道正确的方法,我将非常感谢您的评论
#include <cstring>
#include <iostream>
#include <string>
#include <type_traits>
union U {
double d;
const char* cp;
};
struct A {
enum State { kString, kDouble };
State state;
U u;
void Set(const char* cp) {
u.cp = cp;
state = kString;
}
std::string ToString() const {
switch (state) {
case A::kString : return std::string(u.cp); break;
case A::kDouble : return std::to_string(u.d); break;
default : return "Invalid State";
}
}
};
namespace to_s { using std::to_string; };
int main() {
using namespace to_s;
std::string str = "a nice string";
double d = 1.1;
A a { A::kDouble, {1.2} };
std::cout << "str: " << to_string(str) << ", d: " << to_string(d) << std::endl;
std::cout << "a: " << to_string(a) << std::endl;
a.Set(str.c_str());
std::cout << "a: " << to_string(a) << std::endl;
std::memset(&a, 'i', sizeof(a));
std::cout << "a: " << to_string(a) << std::endl;
}
#包括
#包括
#包括
#包括
联合大学{
双d;
常量字符*cp;
};
结构A{
枚举状态{kString,kDouble};
国家;
U U;
无效集(常量字符*cp){
u、 cp=cp;
state=kString;
}
std::string ToString()常量{
开关(状态){
案例A::kString:返回std::string(u.cp);break;
案例A::kDouble:返回std::to_字符串(u.d);中断;
默认值:返回“无效状态”;
}
}
};
命名空间到{using std::to_string;};
int main(){
使用名称空间来_s;
std::string str=“漂亮的字符串”;
双d=1.1;
A{A::kDouble,{1.2};
std::cout这已经有了一个很好的答案,但我想提出一个替代方案,欢迎反馈
如果您对to_字符串函数名不是死板的,您可以实现自己的ToString free函数模板,并对to_字符串支持的类型进行专门化:
template<class T>
std::string ToString(const T& t)
{
std::ostringstream stream;
const uint8_t* pointer = &t;
for(size_t i=0;i<sizeof(T);++i)
{
stream << "0x" << std::hex << pointer[i];
}
return stream.str();
}
template<> std::string ToString(const int& t) { return std::to_string(t); }
template<> std::string ToString(const long& t) { return std::to_string(t); }
template<> std::string ToString(const long long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long long& t) { return std::to_string(t); }
template<> std::string ToString(const float& t) { return std::to_string(t); }
template<> std::string ToString(const double& t) { return std::to_string(t); }
模板
标准::字符串到字符串(常量T&T)
{
std::奥斯汀溪流;
const uint8_t*指针=&t;
对于(size_t i=0;iRelated也@ShafikYaghmour可能是..但是,那里的解决方案试图重载到_字符串,而不是使类_stringable@Lol4t0不,我的目的不是通过将其转换为string来实现序列化。我不相信有一个重载std::to_string
可以用于您想要的内容。在C++中,与C/y/java不同,没有任何类继承自所有的基类,因此没有可重写/可重载的<代码> toStReg()/<代码>方法。是的,这是真的。但是,例如,我不能这样做:'相应地编辑了答案,只需将打印字符串
的行为放入操作符,谢谢,但是在两个不同的名称空间中有多个字符串不是太容易混淆吗?没有办法使用相同的名称空间吗?@HumamHelfawi没有,不幸的是你不能在std
中为你的类定义到字符串
。我认为最后一部分引号的长度(不是粗体)很重要。如果std::to_string()
是作为模板实现的,您可以为您的用户定义类型专门化std::to_string()
。但是,它是作为重载函数实现的,所以您不能。很高兴知道std
库被标记为“不可由外部软件扩展”(一般而言)。这比Richard的解决方案好多少?@Slava它不需要在每次使用to_string
之前手动使用std::to_string
;您不必污染名称空间。相反,污染只在notstd::adl_helper
名称空间内完成。每次只需调用notstd::to_string
。我使用一个友元来创建字符串,但这相当于一个免费函数
class my_class{
public:
friend std::string to_string(my_class const& self) {
return "I am " + notstd::to_string(self.i);
}
int i;
};
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
template<>
std::string to_string(const std::string& t) {
return t;
}
#include <cstring>
#include <iostream>
#include <string>
#include <type_traits>
union U {
double d;
const char* cp;
};
struct A {
enum State { kString, kDouble };
State state;
U u;
void Set(const char* cp) {
u.cp = cp;
state = kString;
}
std::string ToString() const {
switch (state) {
case A::kString : return std::string(u.cp); break;
case A::kDouble : return std::to_string(u.d); break;
default : return "Invalid State";
}
}
};
namespace to_s { using std::to_string; };
int main() {
using namespace to_s;
std::string str = "a nice string";
double d = 1.1;
A a { A::kDouble, {1.2} };
std::cout << "str: " << to_string(str) << ", d: " << to_string(d) << std::endl;
std::cout << "a: " << to_string(a) << std::endl;
a.Set(str.c_str());
std::cout << "a: " << to_string(a) << std::endl;
std::memset(&a, 'i', sizeof(a));
std::cout << "a: " << to_string(a) << std::endl;
}
template<class T>
std::string ToString(const T& t)
{
std::ostringstream stream;
const uint8_t* pointer = &t;
for(size_t i=0;i<sizeof(T);++i)
{
stream << "0x" << std::hex << pointer[i];
}
return stream.str();
}
template<> std::string ToString(const int& t) { return std::to_string(t); }
template<> std::string ToString(const long& t) { return std::to_string(t); }
template<> std::string ToString(const long long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long long& t) { return std::to_string(t); }
template<> std::string ToString(const float& t) { return std::to_string(t); }
template<> std::string ToString(const double& t) { return std::to_string(t); }
template<> std::string ToString(const my_class& t) { return "I am " + std::to_string(t.i); }