10

Edit: I have reformatted the post to be clearer.

Why does this work:

struct A {};

struct B {
    B(A){}
};

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

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

and this does not?

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";
}

Essentially, in the first example a1 and a2 both convert to B objects through the implicit conversion and use the operator+(const B&, const B&) to add.

Following from this example, I would have expected "Hello" and "world" to convert to B objects, again through the implicit constructor, and use operator+(const B&, const B&) to add to each other. Instead there is an error, which indicates the C-style strings do not attempt a user-defined conversion to B in order to add. Why is this? Is there a fundamental property that prevents this?

9
  • 1
    You didn't involve std::string with "stack" + "overflow";. Commented Oct 18, 2015 at 19:45
  • 1
    @πάνταῥεῖ He is aware of that, the question is why the implicit conversion to std::string does not fix that for him. Commented Oct 18, 2015 at 19:59
  • 1
    Your minimal example is a little bit off as const char* has no conversion operator, but the conversion happens via std::strings constructor. This would be a little more apt. Commented Oct 18, 2015 at 20:06
  • 1
    stackoverflow.com/questions/6677072/… Commented Oct 18, 2015 at 20:07
  • Oops, that dupe does not explain it. Sorry. coliru.stacked-crooked.com/a/0c8bc9e13e198e7b Commented Oct 18, 2015 at 20:56

2 Answers 2

5

In your first example, overload resolution is allowed to find your operator+:

[C++14: 13.3.1.2/2]: If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. [..]

[C++14: 13.3.2/1]: From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit (13.3.3). The selection of viable functions considers relationships between arguments and function parameters other than the ranking of conversion sequences.

[C++14: 13.3.2/2]: First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.

  • If there are m arguments in the list, all candidate functions having exactly m parameters are viable.
  • [..]

[C++14: 13.3.2/3]: Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. [..]

(You may examine the wording for "implicit conversion sequence" yourself to see that the operator+ call is permissible; the rules are too verbose to warrant verbatim reproduction here.)

However, in your second example, overload resolution is constrained to a basic arithmetic addition mechanism (one which is not defined for const char[N] or const char*), effectively prohibiting any operator+ function from being considered:

[C++14: 13.3.1.2/1]: If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to Clause 5.

[C++14: 5.7/1]: [..] For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type. [..]

[C++14: 5.7/3]: The result of the binary + operator is the sum of the operands.

Sign up to request clarification or add additional context in comments.

1 Comment

Good answer. Thanks to you and @BaummitAugen especially for helping with this (and everyone else too).
2

1. Explaining your compiler error:

The reason you can't concatenate two string literals using the '+' operator,
is because string literals are simply arrays of characters, and you can't concatenate two arrays.

Arrays will be implicitly converted to the pointer of their first element.

Or as the standard describes it:

[conv.array]
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

What you are really doing in the example above,
is trying to add two const char pointers together, and that is not possible.


2. Why the string literals aren't implicitly converted:

Since arrays and pointers are fundamental types, you can't provide an implicit conversation operator as you have done in your class example.

The main thing to keep in mind, is that std::string knows how to take in char[], but char[] does not know how to become a std::string. In your example, you've used B, as a replacement to char[], but you've also given it the ability to convert itself to A.


3. Alternatives:

You can concatenate string literals by leaving out the plus operator.

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

Optionally, you could make "stack" a std::string, and then use the std::string's overloaded '+' operator:

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

6 Comments

This does not explain why the implicit conversion to std::string does not happen.
As @BaummitAugen mentions, this answer doesn't explain why both char* operands couldn't be converted to string by user-defined conversions and operator+(const string&, const string&) be used.
Can you cite the relevant section of the C++ standard? Because there's an answer that does and it's completely different (based on string being a template).
@BaummitAugen I was addressing their statement: "I would expect a conversion of both C-style strings to a string", which I believe is the crux of their question. My answer fits what the title question is asking. It's true that they go on to emulate their desired behavior using non-fundamental types, and then reach an unrelated issue similar to the one marked as a duplicate. But the problem they ran into while experimenting, is not the same as the question they are asking.
"I was addressing their statement: "I would expect a conversion of both C-style strings to a string"" You are only addressing this is the the second part of your answer, and that part is wrong.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.