8

I have the following class:

#include <iostream>
#include <string>

using namespace std;

class CLS
{
   int value;
   string str_value;

public:

    CLS(int param) { value = param; }

    CLS(string param)
    {
       str_value = param;
    }
};

int main()
{
    CLS a(2);
    CLS b = 3;
    CLS c("4");
    CLS d = "5";  // Error: invalid conversion from 'const char*' to 'int'
}

I searched for the reason why it is error with no luck.

Is it correct to construct with a string literal? if no, why? if yes, what is wrong with my code?

I am using gcc 5.3 with Code::Blocks 16.1.

Kamal Zidan
  • 474
  • 2
  • 9
  • 5
    I note that this builds just fine under VS2017. It is worth remembering that a string literal is not the same as a `std::string`. – Rook Jun 10 '17 at 12:21
  • Is that a full example? Any headers included? What is `string` in your case? – ArturFH Jun 10 '17 at 12:25
  • @Artur R. Czechowski - It is a full example (I added the headers). `string` should be `std::string` I guess. – Kamal Zidan Jun 10 '17 at 12:33
  • 1
    Try adding a constructor for `const char*`. As @Artur R. Czechowski said, a `std::string` is not the same as a string literal. – Gambit Jun 10 '17 at 12:37
  • 2
    @Rook MSVC has an extension to allow multiple implicit user-defined conversions. Cranking up the warning level should produce a diagnostic. – Quentin Jun 10 '17 at 15:14
  • @Quentin interesting, thanks. – Rook Jun 10 '17 at 16:53

2 Answers2

11

At first, "4" is not std::string, it's const char[2]. Then

CLS c("4"); is direct initialization, the constructors of CLS will be examined for initializing c. CLS::CLS(string) is picked up here, because const char[] can be implicitly converted to std::string via user-defined conversion (i.e. by std::string::string(const char*)).

CLS d = "5"; is copy initialization,

(emphasis mine)

  • If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution.

That means user-defined conversion sequences is required to convert const char[2] to CLS. Even const char[] could be converted to std::string, and std::string could be converted to CLS, but only one user-defined conversion is allowed in one implicit conversion sequence. That's why it's rejected.

(emphasis mine)

Implicit conversion sequence consists of the following, in this order:

1) zero or one standard conversion sequence;
2) zero or one user-defined conversion;
3) zero or one standard conversion sequence.

BTW: If you change it to using std::string as the initializer expression explicitly it'll work fine. e.g.

CLS d = std::string{"5"};  // pass a temporary std::string constructed from "5" to the constructor of CLS

using namespace std::string_literals;
CLS d = "5"s;              // "5"s is a string literal of type std::string (supported from C++14)
T.C.
  • 133,968
  • 17
  • 288
  • 421
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • So the one user-defined conversion requirement also applies to implicit conversions? Because otherwise `const char[2]` -> implicit `const char*` -> user-defined `std::string`, right? – Rakete1111 Jun 10 '17 at 13:16
  • @Rakete1111 (1) Yes, only one user-defined conversion is allowed in one implicit conversion sequence; (2) I can't get your intent, `const char[2]` -> `const char*` is not user-defined conversion but standard conversion which could be more than once. – songyuanyao Jun 10 '17 at 13:20
  • Yes, that's exactly my point: If only 1 user defined conversion is allowed, then `const char[2]` -> `const char*` doesn't count, and `std::string`'s constructor can be used (which is 1 user defined conversion). But why is it not allowed then? – Rakete1111 Jun 10 '17 at 13:27
  • @Rakete1111 That's because we need to convert `const char[2]` to `CLS` for copy initialization. – songyuanyao Jun 10 '17 at 13:30
  • Oh, so no intermediate conversion is allowed? – Rakete1111 Jun 10 '17 at 13:59
  • 1
    @Rakete1111 Yes, otherwise user-defined conversions would be able to be chained. – songyuanyao Jun 10 '17 at 14:20
8
CLS a(2); 
CLS b = 3;
CLS c("4");
CLS d = "5";

aand c are initialised with direct initialisation. b and d on the other hand use copy initialisation.

The difference is that for copy initialisation the compiler searches for a (single) user defined conversion from (in case of d) char const * (this is a bit inaccurate, see end of answer) to CLS, whereas for direct initialisation all constructors are tried, where CLS(std::string) can be used​ because there's a conversion std::string(char const *) available.

Detail:

"5" is a (C) string literal. It's type is char const [2]. So first, a user defined conversion from that type to CLS is searched. Because none is found, the standard conversion from Type[N] to Type * (with Type = char const and N = 2) is applied, resulting in a char const *. Then the compiler tries to find a user defined conversion from that to CLS. Because it doesn't find one, and there are no more standard conversions it could try available, compilation fails.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • You mean "[...] user defined conversion from (in case of `d`) `char const[]` [...]"? `"5"` can decay to `const char*`, but it is a `const char[2]`. – Rakete1111 Jun 10 '17 at 13:13
  • @Rakete1111 there you go, I initially left decay out of the answer to keep it focussed on the conversions. – Daniel Jour Jun 10 '17 at 13:47