4
public:
    string str;

    Test(string& str){
        this->str=str;
        cout<<"constructor"<<endl;
    }
 

};

int main() {
    Test t="test";
    return 0;
}

In this code I get the error "..\src\Test.cpp:30:9: error: conversion from 'const char [5]' to non-scalar type 'Test' requested "?

Why is then the following code ok?

#include <iostream>
using namespace std;

class Test{

public:
    string str;

    Test(string str){
        this->str=str;
        cout<<"constructor"<<endl;
    }

    Test(const Test &test){
        cout<<"copy constructor"<<endl;
        this->str=test.str;
    }

};

int main() {
    Test t=Test("test");
    return 0;
}
Løiten
  • 3,185
  • 4
  • 24
  • 36
jiafu
  • 6,338
  • 12
  • 49
  • 73

4 Answers4

9
Test t="test";

This tries to:

  • Convert the string literal (which is an array of characters, char[5]) into a temporary string
  • Convert the temporary string to Test

This fails for two reasons:

  • An implicit conversion sequence can't involve more than one user-defined conversion. Two are needed here.
  • A temporary can't bind to a non-const lvalue reference, which is what your conversion constructor Test(string&) wants.

Your second example fixes these problems by:

  • Making the conversion from string to Test explicit, Test(...). Now there is only one implicit conversion, from char[5] to string. Note that this is rather a verbose way to initialise it; Test t("test") would do the same thing.
  • Declaring the conversion constructor to take string by value, not reference. A const reference, const string&, would also work, as would a const char*.

Both of these changes are necessary, since each fixes just one of the two problems.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    this always troubled me , now assigning a string literal, why will the compiler create a temp string object? is it language defined thing to be as such? and during creation of object if the obect has overloaded `=` why it triggers copy assignment constructor instead of the overloaded operator? – Koushik Shetty Apr 27 '13 at 13:22
  • @Koushik: Because the only suitable constructor for `Test` (once it's fixed to take a value or `const` reference) requires a string object. If there were a constructor taking `const char*`, then the literal could be passed directly to that without creating a temporary. – Mike Seymour Apr 27 '13 at 13:24
  • In your comments to another answer you say const reference to string would not work, as I state in my answer. [GCC agrees](http://ideone.com/7YYR08) unless I missed something. So, which one is it? – juanchopanza Apr 27 '13 at 13:24
  • @juanchopanza: *Just* changing the constructor to take a const reference won't work, because there are two problems. You need a constructor taking a value or const reference *and* an explicit conversion to make the conversion sequence valid - as my answer says. – Mike Seymour Apr 27 '13 at 13:26
  • Oh I see, I missed the **and**. – juanchopanza Apr 27 '13 at 13:27
  • @MikeSeymour even if the constructor has `const string&` it should work right? and why in `Test t="test";` the constructor is invoked if `= is overloaded?` doesnt the above statement convert to something like `Test t; t = "literal"`? – Koushik Shetty Apr 27 '13 at 13:30
  • @Koushik: As for your second question, initialisation is not assignment even if it involves the `=` token, so initialisation is done using a constructor, not an assignment operator. – Mike Seymour Apr 27 '13 at 13:31
  • @Koushik: `Test t="test"` won't work because it requires two user-defined conversions, `char[5]` to `string`, and `string` to `Test`. Only one is allowed in an implicit conversion sequence - as my answer says. – Mike Seymour Apr 27 '13 at 13:32
  • ah thanks. yes the first part of the question is clear from your answer. thanks again. – Koushik Shetty Apr 27 '13 at 13:33
  • I wonder why the `const char*` variant works then (see [demo](http://ideone.com/jsRifp)). In principle it involves the same number of conversions, yet g++ allows it. I based my answer on that but now I am not so sure it is correct. – juanchopanza Apr 27 '13 at 13:36
  • @juanchopanza: It only requires one user-defined conversion, `const char*` to `Test`. The array-to-pointer conversion is a standard conversion, and an implicit conversion can contain as many standard conversions as it needs. – Mike Seymour Apr 27 '13 at 13:38
  • Of course, thanks. I would say I haven't had enough coffee yet but I don't even have that excuse. – juanchopanza Apr 27 '13 at 13:39
  • @MikeSeymour **Test t = std::string("test");** why it's not calling copy constructor. std::string("test") will create a temp test obj and copy initialize **t** using that temp obj, so copy constructor should be called at this time. What I'm missing here.? – Vencat Sep 08 '19 at 05:03
2

You are only allowed one implicit conversion between user defined types. Your code has an implicit conversion from const char[6] (which decays to const char*) to std::string, and from std::string to Test. This is the reason changing the signature to Test(const std::string&) will not work.

Try changing the constructor signature to

#include <string>

struct Test
{
  Test(const char* c) : s_(c) {}
  std::string s_;
};

int main()
{
  Test t = "Hello";
}
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
0

Just put a const to accept "test" which is a constant literal :

Test(const string& str) : str(str) {
     ^^^^^
}

and use it like

Test t("test");

or

Test t = string("test");

Live code

masoud
  • 55,379
  • 16
  • 141
  • 208
  • @MikeSeymour it will on MSVC, yes this is flaw of MS compiler, but I think he tried. At least I did, compiled without single warning. – alexrider Apr 27 '13 at 13:26
0

Test t="test"; The complier will try to find a construtor: Test(const char*), but you just define a Test(string&), so it popup the error.

your can try to define a constructor such as Test(const char*) or modify your code like this: Test t = string("test")