【问题标题】:C++ addition overload ambiguityC++ 加法重载歧义
【发布时间】:2010-04-10 14:08:35
【问题描述】:

我的代码库中遇到了一个令人头疼的难题。我不太清楚为什么我的代码会生成此错误,但(例如)std::string 不会。

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval );
};

这些的实现很容易自己想象。

我的驱动程序包含以下内容:

String result, lval("left side "), rval("of string");
char lv[] = "right side ", rv[] = "of string";
result = lv + rval;
printf(result);
result = (lval + rv);
printf(result);

这会在 gcc 4.1.2 中产生以下错误:

driver.cpp:25: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
String.h:22: note: candidate 1: String operator+(const String&, const char*)
String.h:24: note: candidate 2: String String::operator+(const String&)

到目前为止一切都很好,对吧?可悲的是,我的 String(const char *str) 构造函数作为隐式构造函数非常方便,使用显式关键字来解决这个问题只会导致一堆不同的问题。

此外... std::string 不必诉诸于此,我不知道为什么。例如,在 basic_string.h 中,它们的声明如下:

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>
operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
          const basic_string<_CharT, _Traits, _Alloc>& __rhs)

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT,_Traits,_Alloc>
operator+(const _CharT* __lhs,
          const basic_string<_CharT,_Traits,_Alloc>& __rhs);

等等。 basic_string 构造函数未显式声明。这怎么不会导致我遇到同样的错误,我怎样才能实现同样的行为??

【问题讨论】:

    标签: c++ gcc operator-overloading stdstring


    【解决方案1】:

    产生歧义的原因是,只有当一个候选函数的 none 参数比另一个候选函数的参数匹配更差时,一个候选函数才比另一个候选函数好。考虑您的两个功能:

    friend String operator+(const String&, const char*); // (a)
    String operator+(const String&);                     // (b)
    

    您正在使用Stringconst char* 调用operator+

    const char* 类型的第二个参数显然比 (b) 更好地匹配 (a)。它与 (a) 完全匹配,但 (b) 需要用户定义的转换。

    因此,为了产生歧义,第一个参数必须比 (a) 更好地匹配 (b)。

    operator+ 调用左侧的 String 不是 const。因此,它匹配 (b),它是一个非常量成员函数,优于 (a),它采用 const String&amp;

    因此,以下任何一种解决方案都可以消除歧义:

    • 将成员 operator+ 更改为 const 成员函数
    • 将非成员operator+ 更改为String&amp; 而不是const String&amp;
    • 调用operator+,左侧带有一个常量字符串

    显然,第一个 also suggested by UncleBens 是最好的选择。

    【讨论】:

    • 我什至没有想到 const-ness 会导致匹配不佳。你和本叔叔(当然)完全正确。另一种解决方案实际上是使成员成为接受两个 const 字符串的非成员 - 这实际上就是 std::string 版本没有错误的原因。当然,这相当于你的第一个建议,只是写法不同而已。
    【解决方案2】:

    在这种情况下,只需在 operator+ 上定义就足够了:

    String operator+(const String& lval, const String& rval);
    

    因为您提供了一个采用char* 的构造函数,所以可以在调用operator+ 期间从char* 构造一个String。例如:

    String hello = "Hello, ";
    const char* world = "world!";
    
    String helloWorld = hello + world;
    

    将使用char* world 的内容构造一个临时的String(因为您的构造函数不是显式的),然后将两个String 对象传递给operator+

    【讨论】:

    • 这会创建一个无用的临时对象,可能会花费大量时间和内存。
    • @Daniel Newby:不是真的,因为编译器能够经常避开副本。此外,正如 OP 在其原始帖子中所要求的那样,“相当大的时间和内存成本”正是 std::string 所做的。
    【解决方案3】:

    如果您按应有的方式声明成员 + const,错误就会消失。

    class String {
    public:
        String(const char*str);
        friend String operator+ ( const String& lval, const char *rval );
        friend String operator+ ( const char *lval, const String& rval );
        String operator+ ( const String& rval ) const; //<-- here
    };
    

    虽然不知道是什么原因。如果可能,它可能更喜欢将参数绑定到 const 引用,因此第一个重载更适合左侧值,而第三个重载更适合右侧值。

    Better explanation.(一定是误读了问题。)


    printf(result);
    

    别告诉我你的 String 隐式转换为 const char*... 这太邪恶了。

    【讨论】:

    • 希望这是通过 ADL 找到的非标准 printf 函数。虽然它应该是puts,而不是printf,但它所做的只是打印参数。
    • puts 添加了一个换行符。如果不需要换行符,我想应该是printf("%s", str.c_str());
    • +1 这可以说是最好的选择。我试图解释歧义:stackoverflow.com/questions/2613645/…
    • 虽然我知道你为什么这么说,但我认为邪恶在旁观者的眼中;)
    【解决方案4】:

    模板和非模板函数遵循不同的规则。模板函数是在实际参数类型上选择的,没有应用任何转换。对于非模板(即您的代码),可以应用隐式转换。因此,basic_string 中的模板化内容并不模棱两可,但您的却是。

    【讨论】:

      【解决方案5】:

      您已经证明basic_string 具有operator+ 的实现,对应于您的类String 中的第二个和第三个运算符。 basic_string 是否也有对应于您的第一个运算符 [friend String operator+ ( const String&amp; lval, const char *rval );] 的运算符?

      如果删除此运算符会怎样?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-29
        • 1970-01-01
        相关资源
        最近更新 更多