kapil
kapil

Reputation: 271

Is it possible to write explicit constructor that facilitates copy initialisation?

I am actually writing an implementation of string and am facing a confusing scenario while trying to construct string from single character.

mystring.h

  class string final {
    private:
      static constexpr std::size_t default_capacity_ = 0;
      std::size_t current_capacity_;
      std::size_t sz_;
      std::unique_ptr<char[]> ptr_;
    public:
      explicit string(char);  // [1]
      string();
      string(const string&);
      string(string&&) noexcept;
      string(const char*);  // Undefined behavior if the input parameter is nullptr.
      ~string() noexcept;
  };

mystring.cpp

  string::string(char ch) {
    sz_ = 1;
    current_capacity_ = get_appropriate_capacity(sz_);
    ptr_ = std::make_unique<char[]>(current_capacity_ + 1);
    ptr_.get()[0] = ch;
    ptr_.get()[1] = '\0';
  }

  string::string(const char* c_string) {    // [2]
    sz_ = std::strlen(c_string);
    current_capacity_ = get_appropriate_capacity(sz_);
    ptr_ = std::make_unique<char[]>(current_capacity_ + 1);
    std::memcpy(ptr_.get(), c_string, sz_ + 1);
  }

test.cpp

#include "my_string.h"

using namespace kapil;

int main() {
  string z8 = 'c';  // [3]
  return 0;
}

In this example, [3] doesn't compile as the constructor string(char ch) for string is explicit. [3] gives the following error :

error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
   string z8 = 'c';
               ^~~
In file included from test_string.cpp:1:0:
my_string.h:22:7: note:   initializing argument 1 of ‘kapil::string::string(const char*)’
       string(const char*);  // Undefined behavior if the input parameter is nullptr.

Making it non-explicit will allow the code to work, but it will also allow statements like below :

string s = 144;  // [4]

I have following questions in this context :

[a] Is there a way, using 'explicit' constructor, to enable string s = 's'; but not string s = 144;

[b] The error caused due to [4] shows that it tries to match the constructor call with string(const char*) , why is it so ? provided we have a constructor string(char ch).

[c] If there is no way to achieve [a] with 'explicit' constructor, then what is the (right) way to achieve it.

P.S. This code shows partial implementation. Kindly visit https://github.com/singhkapil2905/cplusplus-string-implementation to see complete implementation.

Thanks for any help :)

Upvotes: 1

Views: 51

Answers (1)

Quentin
Quentin

Reputation: 63114

The compiler tries to match the string(char const *) constructor simply because the other one is explicit, and thus ignored in copy-initialization.

To allow copy-initialization from a char, the first step is indeed to implement string(char) as non-explicit. Then what you need to prevent copy-initialization from an int is to avoid the conversion to char. You can do that simply by providing and deleting a constructor that is a better match:

string(int) = delete;

Note that in any case, string s('a'); works with an explicit constructor as well since it is direct-initialization.

Upvotes: 5

Related Questions