24

I have extended std::string to fulfil my needs of having to write custom function build into string class called CustomString

I have defined constructors:

    class CustomString : public std::string {
    public:
        explicit CustomString(void);
        explicit CustomString(const std::string& str);
        explicit CustomString(const CustomString& customString);
        //assignment operator
        CustomString& operator=(const CustomString& customString);
    ... };

In the third constructor (copy constructor) and assignment operator, whose definition is:

CustomString::CustomString(const CustomString& customString):
    std::string(static_cast<std::string>(customString)) 
{}
CustomString& CustomString::operator=(const CustomString& customString){
    this->assign(static_cast<std::string>(customString));
    return *this;
}

First since this is an "explicit"; meaning an explicit cast is needed to assign to another CustomString object; it's complaining about the assignment.

CustomString s = CustomString("test");

I am not sure where exactly is casting needed explicitly.

The code works alright if copy constructor is not explicit but I would like to know and implement explicit definition instead of "guessing proper cast".

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
abumusamq
  • 780
  • 1
  • 10
  • 29

3 Answers3

50

The explicit copy constructor means that the copy constructor will not be called implicitly, which is what happens in the expression:

CustomString s = CustomString("test");

This expression literally means: create a temporary CustomString using the constructor that takes a const char*. Implicitly call the copy constructor of CustomString to copy from that temporary into s.

Now, if the code was correct (i.e. if the copy constructor was not explicit), the compiler would avoid the creation of the temporary and elide the copy by constructing s directly with the string literal. But the compiler must still check that the construction can be done and fails there.

You can call the copy constructor explicitly:

CustomString s( CustomString("test") );

But I would recommend that you avoid the temporary altogether and just create s with the const char*:

CustomString s( "test" );

Which is what the compiler would do anyway...

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    Oh yea I completely understand your answer (and it is proper answer and I have marked it as best answer) but how would I achieve `CustomString s = customStringObjectOnStack;` for example? – abumusamq Jul 14 '12 at 04:32
  • @mkhan3189 you can achieve CustomString s = customStringObjectOnStack if and only if copy-ctor will not be explicit. For second question, kDelimited is CustomString or something else? And + your function is const, so c.append(static_cast(*this)) will works. – ForEveR Jul 14 '12 at 04:56
  • @ForEveR Yea Thanks for that. I got the answer myself. kDelimiter is CustomString constant object, yes. `c.append(static_cast(*this))` works for implicit copy-ctor declaration. So all good. This thread can be closed. – abumusamq Jul 14 '12 at 05:06
  • 5
    @mkhan3189: You can achieve `CustomString s = customStringObjectOnStack;` by instead of implicitly using that copy constructor with the `=` syntax, you explicitly call the copy constructor: `CustomString s( customStringObjectOnStack );` – David Rodríguez - dribeas Jul 14 '12 at 15:55
7

Deriving from std::string is not safe, as std::string has no virtual destructor. As to your question - your copy constructors should not be explicit, to allow for such usage as:

CustomString s = "test";

Also I have no idea why you would want to declare a copy-constructor as explicit, as it is not needed. An explicit copy-constructor will work only if you declare your CustomString object as:

CustomString s(CustomString("test"));
metamorphosis
  • 1,972
  • 16
  • 25
ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • @ildjarn: why lazy? It is correct (not optimal, but correct) tackles the problem and provides a solution... – David Rodríguez - dribeas Jul 14 '12 at 02:58
  • @DavidRodríguez-dribeas when ildjarn posting comment, answer was rather shorter. – ForEveR Jul 14 '12 at 03:11
  • Thanks and nice answer but this is lazy because I have asked this to improve my way of writing code instead of just getting thing done. Thanks anyways :) – abumusamq Jul 14 '12 at 04:35
  • 3
    As long as the OP _only_ uses `CustomString`s and doesn't, say, try to `delete` a `new CustomString` through a `std::string` pointer, is there _really_ anything unsafe about this derivation? – Joshua Green Oct 22 '14 at 02:21
  • *“As long as [...], is there anything unsafe?”*. Unless you have the means to enforce the conditions reliably and consistently over time, including coworker's code and wherever this might get copy-pasted to in the future, the answer to that question is always yes. No matter what's in the "[...]". – spectras Aug 21 '20 at 15:00
0

It seems current gcc won't invoke the copy constructor tested in gcc 8.2(MinGW.org GCC-8.2.0-5),and will utilize to call constructor directly For X1

#include <iostream>

using std::cout;
using std::endl;


class X1 {
public:

    X1() {
        cout<< "invoke constructor" << endl;
    }; 

    explicit X1(const X1 &obj)  { 
        cout << "invoke copy constructor" << endl;
    };

    X1(X1&& obj)  {
        cout << "invoke move constructor" << endl;
    };

    X1& operator=(const X1 &obj) {
        cout << "invoke value assign constructor " << endl;
        if(this == &obj) {
            return *this;
        }
        return *this;
    };

    X1& operator=(const X1 && obj) {
        cout << "invoke move value assign constructor " << endl;
        if(this == &obj) {
            return *this;
        }
        return *this;

    };

    ~X1() {
        cout << "invoke deconstruct" << endl;
    };



};

int main() {
//    X1 l1(1);
//    X1 l3(2);
    X1 l4 = X1();

    return 0;
}
truejasonxiefans
  • 179
  • 2
  • 12