C++ 将自定义类型的转换运算符重载为 std::string
Posted
技术标签:
【中文标题】C++ 将自定义类型的转换运算符重载为 std::string【英文标题】:C++ overloading conversion operator for custom type to std::string 【发布时间】:2011-03-31 21:29:07 【问题描述】:我希望有人能够回答为什么以下内容不起作用。不过请耐心等待,我仍然是一个菜鸟…… 我只是无法深入了解以下原因
using namespace std;
#include <string>
#include <iostream>
class testClass
public:
operator char* () return (char*)"hi";;
operator int () return 77;;
operator std::string () return "hello";;
;
int main()
char* c;
int i;
std::string s = "goodday";
testClass t;
c = t;
i = t;
s = t;
cout<< "char: " << c << " int: " << i << " string: "<<s<<endl;
return 0;
给我一个编译时错误:
myMain.cpp: In function ‘int main()’:
myMain.cpp:23: error: ambiguous overload for ‘operator=’ in ‘s = t’
/usr/include/c++/4.2.1/bits/basic_string.h:500: note: candidates are: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:508: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:519: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
如果我不尝试分配
s = t;
确实有效。
我已经尝试了几个小时来理解错误消息,但最让我困惑的是它确实适用于 char*。
我很感激任何提示。 谢谢! 马库斯
【问题讨论】:
【参考方案1】:错误试图解释的是,如果t
也是std::string
,或者t
是一个 [const
] char*
。您的转换运算符可以将t
转换为任何一个,因此编译器没有依据来选择一个而不是另一个......
您可以通过选择所需的转换来明确消除歧义:
s = t.operator std::string();
s = static_cast<std::string>(t);
或者您可以只提供一种转化,并让用户在必要时进行进一步的转化。
你可能会发现 - 最后 - 任何转换运算符都比它的价值更麻烦......这说明std::string
本身并没有为const char*
提供转换运算符。
【讨论】:
但即使我将变量 c 和 char* 运算符排除在等式之外,它也无法编译。我试图避免类 testClass 的用户明确消除歧义,以解决无法重载返回值不同的成员函数的问题。 根据错误消息,歧义中涉及的第三个 std::string 赋值运算符来自“char”类型......不幸的是,您的测试类隐式转换为 int,而 char 只是一个(通常)8位整数......我同情你的目标......如果存在一个好的解决方案会很棒 - 想象一下不必经常调用 std::string::c_str() - 但不幸的是可用的方法有问题。也许 C++ 应该支持一种符号来消除这种情况的歧义——当程序员确信所有匹配在功能上是等效的——但现在...... :-(. “功能等效”并不意味着等效。使用 const std::string& 构造/分配字符串可能比 const char* 更有效(我认为,使用 GCC libstdc++,它共享缓冲区并进行写时复制),即使它们似乎在执行同样的事情。 非常正确。类似地,std::string::size() 是 O(1) 而 strlen() 是 O(n)。所以,假设你已经重载了 fn(const std::string&) 和 fn(const char*),如果你可以表明对前者的偏好来指定使用哪个参数的转换运算符,那么被调用者将只是一次做出最佳选择,而不是调用者必须在每个调用站点都这样做,可能不知道 fn() 实现以及哪个重载调用更可取。【参考方案2】:$13.3.1.5/2 声明-“转换 S及其基类的函数 被考虑。那些不是 隐藏在 S 中并产生类型 T 或 a 可以转换为类型 T 的类型 通过标准转换序列 (13.3.3.1.1) 是候选函数。 返回一个转换函数 cv 限定类型被认为是 产生 cv 不合格的版本 这种类型的这个过程 选择候选函数。 返回的转换函数 “引用 cv2 X”返回左值 键入“cv2 X”,因此是 考虑为此过程产生 X 选择候选函数。”
赋值 s = t 的作用如下:
a) 't' (testClass) 类型中的所有成员都被认为可以将't' 转换为's'。
Candidate 1: operator string(); // s created using member string::operator=(string const&)
Candidate 2: operator char *() // s created using member string::operator=(char const*)
Candidate 3: operator char *() // s created using member string::operator=(char *)
b) 上述所有候选者都是可行的(也就是说,在没有其他候选者的情况下,编译器可以成功解析对其中任何一个的函数调用)
c) 但是,现在必须确定最可行的候选人。涉及的转换序列是:
Candidate 1: U1 : operator string()
Candidate 2: U2 : operator char*()->const qualification to match string::operator=(char const*)
Candidate 3: U3 : operator char*()
$13.3.3.1.1/3 状态 - “a 的排名 转换顺序由下式确定 考虑到每个人的等级 在序列和转换 任何参考绑定的等级 (13.3.3.1.4)。如果其中任何一个有 转换秩,序列有 转化排名;"
这意味着 U1、U2 和 U3 都具有转换等级,并且在第一级中,两者都不比另一个更好。但是,该标准还规定
用户定义的转换序列U1是 更好的转换顺序 另一个用户定义的转换 如果它们包含相同的序列 U2 用户定义的转换函数或 构造函数和如果第二个标准 U1的转换顺序更好 比第二个标准转换 U2的序列。
那么,让我们看看这意味着什么。
在 U1 和 U2 之间,它们涉及不同的转换函数,因此没有一个比另一个更好
在 U1 和 U3 之间,它们涉及不同的转换函数,因此没有一个比另一个更好
那么 U1 和 U2 呢?它们涉及相同的转换函数,满足上述“与”条件的第一部分
那么“如果U1的第二个标准转换序列优于U2的第二个标准转换序列”部分呢
在 U2 中,第二个标准转换序列需要 const 限定,而在 U3 中则不需要。 U3的第二个标准转换序列是Exact Match。
但正如标准中的表 9 所述,CV 资格也被认为是精确匹配。
因此,就重载解决方案而言,U2 和 U3 也确实无法区分。
这意味着 U1、U2 和 U3 都非常好,编译器发现解析调用(作为赋值语句的一部分)是模棱两可的,因为没有明确的最佳可行函数
【讨论】:
朋友们,在块引号中格式化步骤 (c) 时遇到困难。需要帮助。【参考方案3】:没有确切的 std::string::operator=。候选人是,转述,
s = (const std::string)(std::string)t;
s = (const char*)t;
s = (char)(int)t;
我认为如果您将其更改为 return const std::string,一切都会奏效。(编辑:我错了。)另外请注意,第一个函数应该返回常量字符 *。如果您需要将字符串文字转换为 char*,那么您做错了;字符串文字是不可可写的。
【讨论】:
刚刚试过了,好像不行。并感谢您的提示。我知道我需要思考和了解更多关于事物何时是或应该是 const... 运算符 std::string() 没有任何问题...它从字符串文字构造一个临时的 std::string ,然后由调用者“拥有”,因此对其进行任何非常量操作不会影响字符串文字本身。但它绝对应该是 operator const char*() 第一个。 显然我的 C++-Fu 没有达到标准。它必须同时选择强制转换和赋值运算符,并且没有理由更喜欢operator=(const std::string&)
而不是operator=(const char*)
。我不确定你是否可以定义全局赋值运算符(即::operator=(std::string&,const testClass&)
,但如果它有效,那可能是一个解决方案。)【参考方案4】:
实际上,这是因为std::string
提供了一个接受const char*
的赋值运算符。
【讨论】:
那么也就是说std::string operator= () 和我的testClass::operator std::string () 正在竞争这个职位? @Markus:正如其他人所解释的那样,赋值运算符是重载的,并且可以处理您的类对其进行隐式转换的多个类型。【参考方案5】:好的,已经非常感谢大家了。我想我已经开始掌握它的窍门了,有点……
首先我不知道这个事实,char 只是一个 8 位 int。感谢您的澄清。
所以我明白了,因为为 std::string 定义了三个赋值运算符,每个运算符在我的表达式的右侧都有不同的参数(string、char*、const char*)
s=t
不知道,必须转换成哪种类型,因为有多个,可能匹配的(对于这个分配给 std::string 的)转换定义与任何一个
operator int () return 77;;
operator std::string () return "hello";;
(因为 char : 8bit int)
或
operator char* () return (char*)"hi";;
operator std::string () return "hello";;
对吗?所以用白痴的话来说,赋值的左侧并没有告诉右侧它期望哪种类型,所以 rhs 必须从它的选项中进行选择,其中一个和其他的一样好? std::string operator= 正在容忍我的意图?
到目前为止一切顺利,我以为我明白了 - 但是,为什么以下内容也会产生歧义?
using namespace std;
#include <string>
#include <iostream>
class testClass
public:
operator float () return float(77.333);;
operator std::string () return "hello";;
;
int main()
std::string s = "goodday";
testClass t;
s = t;
cout<< " string: "<<s <<endl;
return 0;
现在我只定义了一个匹配的转换运算符,对吧? std::string operator= 不能接受浮点数,或者可以吗?还是 float 在某种程度上又等同于 char 的某种变体?
我将代码理解为 's=' 告诉 rhs:“给我一个字符串,char* 或 const char*”
Rhs 检查给定 testClass 实例它可以提供什么,唯一匹配的是 testClass::operator std::string
再次感谢你们的耐心、专业知识和时间,我真的很感激。
【讨论】:
这是因为作为浮点整数标准转换序列的一部分,'float' 可以转换为 'char'。如果您难以理解编译器消息,了解该概念的一种方法是注释掉“operator string()”并检查代码中发生的情况。 VS给出“警告C4244:'argument':从'float'转换为'char',可能丢失数据”。这意味着运算符 float() 是一个候选对象,调用此运算符的匹配项需要从 float 转换为 char 好的,我还有很多东西要学。再次感谢大家!我想我的,我认为的优雅解决方案毕竟行不通......以上是关于C++ 将自定义类型的转换运算符重载为 std::string的主要内容,如果未能解决你的问题,请参考以下文章