为什么添加两个字符串字面量不使用operator+?

10

编辑:我已经重新排版这篇文章,使其更加清晰明了。

为什么这个有效:

struct A {};

struct B {
    B(A){}
};

void operator+(const B&, const B&) {}

int main()
{
    A a1, a2;
    a1 + a2;
}

而这个不行吗?

struct B {
    B(const char*){}
};

void operator+(const B&, const B&) {} //error: invalid operands of types 'const char [6]' and 'const char [6]' to binary 'operator+'|

int main()
{
    "Hello" + "world";
}

基本上,在第一个示例中,a1a2都通过隐式转换转换为B对象,并使用operator+(const B&, const B&)进行相加。

根据这个例子,我原本期望"Hello""world"也会通过隐式构造函数转换为B对象,并使用operator+(const B&, const B&)相互相加。但实际上出现了错误,这表明C风格的字符串没有尝试将其转换为B以进行相加。为什么会这样?是否存在某种基本属性以防止这种情况发生?


1
你没有将 std::string"stack" + "overflow" 结合起来。 - πάντα ῥεῖ
1
@πάνταῥεῖ 他知道这一点,问题是为什么隐式转换为 std::string 对他没有起作用。 - Baum mit Augen
1
你的最小示例有点不对,因为const char*没有转换运算符,但是通过std::string的构造函数进行转换。这个可能更加适合。 - Baum mit Augen
1
https://dev59.com/dGw15IYBdhLWcg3wVaQa - Jonathan Potter
我也已经修改了我的第二个示例以匹配你的。 - Silversonic
显示剩余4条评论
2个回答

5
在你的第一种情况中,重载解析允许找到你的operator+:
[C++14: 13.3.1.2/2]:如果任何一个操作数有类或枚举类型,可能会声明一个用户定义的运算符函数来实现这个运算符,或者需要用户定义的转换来将操作数转换为适合内置运算符的类型。在这种情况下,使用重载解析来确定要调用哪个运算符函数或内置运算符来实现运算符。
[C++14: 13.3.2/1]:从给定上下文(13.3.1)构造的候选函数集中选择一组可行函数,从中选择最佳函数,通过比较最佳匹配的参数转换序列来实现。选择可行函数考虑了除转换序列排名之外的参数和函数参数之间的关系。
[C++14: 13.3.2/2]:首先,为了成为可行的函数,候选函数必须具有足够的参数以与列表中的参数数量相匹配。
[C++14: 13.3.2/3]:其次,对于F来说,对应于F的每个参数都应该存在将该参数转换为隐式转换序列(13.3.3.1)的方法。
但是,在您的第二个例子中,重载解析仅被约束为基本算术加法机制(对于const char[N]或const char*没有定义),从而有效地禁止考虑任何operator+函数:
[C++14: 13.3.1.2/1]: 如果表达式中没有运算符的操作数类型是类或枚举类型,则假定该运算符为内置运算符,并根据第5条款进行解释。
[C++14: 5.7/1]: 对于加法,要么两个操作数都必须是算术类型或未作用域的枚举类型,要么一个操作数必须是指向完全定义的对象类型的指针,另一个操作数必须具有整数或未作用域的枚举类型。
[C++14: 5.7/3]: 二元+运算符的结果是操作数的和。

2
好的回答。特别感谢你和@BaummitAugen的帮助(还有其他所有人)。 - Silversonic

2

1. 解释编译器错误:

你不能使用“+”运算符连接两个字符串字面量,因为字符串字面量只是字符数组,你不能连接两个数组。

数组将被隐式转换为它们第一个元素的指针。

或者如标准所描述的那样:

[conv.array]
类型为“N T 的数组”或“未知界限的 T 数组”的左值或右值可以转换为类型为“T 指针”的 prvalue。结果是数组的第一个元素的指针。

在上面的例子中,你真正要做的事情是尝试将两个常量 char 指针相加,这是不可能的。


2. 为什么字符串字面量不会隐式转换:

由于数组和指针是基本类型,你不能像在类示例中所做的那样提供一个隐式转换运算符。

需要记住的主要事项是,std::string 知道如何接收 char[],但 char[] 不知道如何成为 std::string。在你的示例中,你使用 B 作为 char[] 的替代品,但你也赋予它将自己转换为 A 的能力。


3. 其他选择:

你可以通过省略加号运算符来连接字符串字面量。

"stack" "overflow"; //this will work as you indented

你可以选择将“stack”定义为std::string类型,然后使用std::string的重载‘+’运算符:

std::string("stack") + "overflow"; //this will work

3
这并不能解释为什么不会发生到 std::string 的隐式转换。 - Baum mit Augen
2
正如@BaummitAugen所提到的,这个答案没有解释为什么用户定义的转换不能将两个char*操作数转换为string并使用operator+(const string&, const string&) - Silversonic
你能引用C++标准的相关章节吗?因为有一个回答是这样做的,而且它完全不同(基于字符串是一个模板)。 - David Schwartz
@BaummitAugen 我是在回应他们的声明:“我希望将两个C风格字符串转换为一个字符串”,我相信这是他们问题的关键。我的答案符合标题问题的要求。确实,他们继续使用非基本类型来模拟所需的行为,然后遇到了一个类似于标记为重复的问题。但是,他们在实验中遇到的问题与他们提出的问题不同。 - Trevor Hickey
我在回应他们的声明:“我希望将C风格字符串转换为字符串”,你只回应了这个声明的第二部分,而且那部分是错误的。 - Baum mit Augen
你声称问题是const char[]没有转换运算符,但事实并非如此。http://coliru.stacked-crooked.com/a/1c4aafeb95886b92 - Baum mit Augen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接