【问题标题】:C++ overloading conversion operator for custom type to std::stringC++ 将自定义类型的转换运算符重载为 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*。

我很感激任何提示。 谢谢! 马库斯

【问题讨论】:

    标签: c++ overloading operator-keyword


    【解决方案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&amp;) 而不是operator=(const char*)。我不确定你是否可以定义全局赋值运算符(即::operator=(std::string&amp;,const testClass&amp;),但如果它有效,那可能是一个解决方案。)
    【解决方案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
    • 好的,我还有很多东西要学。再次感谢大家!我想我的,我认为的优雅解决方案毕竟行不通......
    猜你喜欢
    • 2018-04-25
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2014-02-02
    相关资源
    最近更新 更多