Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is better implicit conversion through constructor or explicit function in this case?

I am creating my own class for String using C++ solely for learning purposes.

And I stuck upon the place where I should make a decision. Let me explain the matter.

I have two options of my class. I will post below only relevant pieces of the code because I do not want to distract you from my problem at hand. If in order to help me out you need more info I will gladly provide it.

Option 1

class String {
    size_t _length;
    char* _stringHead;
public:
    String(const std::string&);
    String(const char*);
    String(const char);
};
String operator+(String, const String);

const bool operator==(const String, const String);
const bool operator!=(const String, const String);
const bool operator<(const String, const String);
const bool operator<=(const String, const String);
const bool operator>(const String, const String);
const bool operator>=(const String, const String);

Option 2

class String {
    size_t _length;
    char* _stringHead;
public:
    //irrelevant part of code in Option 2
    String(const std::string&);
    String(const char*);
    String(const char);
    //irrelevant part of code in Option 2
};
String operator+(String, const String&);

const bool operator==(const String&, const String&);
const bool operator!=(const String&, const String&);
const bool operator<(const String&, const String&);
const bool operator<=(const String&, const String&);
const bool operator>(const String&, const String&);
const bool operator>=(const String&, const String&);

//for std::string
String operator+(String, const std::string&);

const bool operator==(const String&, const std::string&);
const bool operator!=(const String&, const std::string&);
const bool operator<(const String&, const std::string&);
const bool operator<=(const String&, const std::string&);
const bool operator>(const String&, const std::string&);
const bool operator>=(const String&, const std::string&);

String operator+(const std::string&, String);

const bool operator==(const std::string&, const String&);
const bool operator!=(const std::string&, const String&);
const bool operator<(const std::string&, const String&);
const bool operator<=(const std::string&, const String&);
const bool operator>(const std::string&, const String&);
const bool operator>=(const std::string&, const String&);
//for std::string

//the same goes for char* and char
...
//the same goes for char* and char

So, as you can see from the Option 1 and Option 2 specs the decision here is about whether to use implicit type conversion which is done with the help of constructors or to type each utility separately for each type with which I want my String type to work.

As far as I can see right now the benefit of using the first approach is that it is easier to implement and maintain. While the second approach may produce better performance results.

I would like to get constructive arguments which approach is better and in what scenarios and which approach hence would you use. I think that the biggest part I am interested here is whether or not the performance benefit of the second approach is reasonable.

like image 483
hellohowdoyoudo Avatar asked Sep 20 '18 18:09

hellohowdoyoudo


People also ask

Should constructors be explicit?

C++ static code analysis: "explicit" should be used on single-parameter constructors and conversion operators.

What is difference between implicit and explicit function call in C++?

In programming, implicit is often used to refer to something that's done for you by other code behind the scenes. Explicit is the manual approach to accomplishing the change you wish to have by writing out the instructions to be done explicitly.

What is a conversion constructor?

A conversion constructor is a single-parameter constructor that is declared without the function specifier explicit . The compiler uses conversion constructors to convert objects from the type of the first parameter to the type of the conversion constructor's class.

What is an implicit conversion?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.


1 Answers

An implicit constructor is used to create an instance of the class type when passing an instance of the parameter type to a method that expects the class type. This implicit conversion is done by calling a constructor of the class.

For example, run this code which is similar to yours:

#include <iostream>

class String {
 public:
  String(const std::string& s) {
    std::cout << "called" << std::endl;
  };
};

std::ostream& operator<< (std::ostream& stream, const String& s) {
  return stream;
}

void hello(String s) {
  std::cout << "Hello " << s; // Outputs "called" before "Hello ".
}

int main() {
  std::string s = "world";
  hello(s); // Uses the implicit conversion constructor.
}

Because a new instance of the class String has to be created every time, it is to be expected that performance will suffer slightly. But, in my opinion, not enough to outweigh the benefits: implicit conversion can greatly simplify the job of a class designer and make using a class easier.

However, keep in mind that there exist situations where team members are more likely to be surprised if a conversion happens automatically than to be helped by the existence of the conversion.

Here is such an example:

#include <iostream>

class String {
 public:
  String(int size) {};
};

std::ostream& operator<< (std::ostream& stream, const String& s) {
    return stream;
}

void hello(String s) {
  std::cout << "Hello " << s; // Prints "Hello " as no error occurs.
}

int main() {
  hello(10); // It still calls the implicit conversion constructor.
}

In the code above, an error message produced by the explicit keyword could save someone a bit of time spent debugging.

Some circumstances in which implicit conversion makes sense are:

  1. The class is cheap enough to construct that you don't care if it's implicitly constructed.
  2. Some classes are conceptually similar to their arguments, such as std::string reflecting the same concept as the const char* it can implicitly convert from, so implicit conversion makes sense, as is the case here.
  3. Some classes become a lot more unpleasant to use if implicit conversion is disabled. Think of having to explicitly construct a std::string every time you want to pass a string literal.

Some circumstances in which implicit conversion makes less sense are:

  1. Construction is expensive.
  2. Classes are conceptually very dissimilar to their arguments. Consider the example with the String and the int.
  3. Construction may have unwanted side effects. For example, an AnsiString class should not implicitly construct from a UnicodeString, since the Unicode-to-ANSI conversion may lose information.

So, my advice in your particular case would be to use conversion because it makes sense, as your class is so similar to std::string and to minimize code duplication, but in the future, use implicit conversion with thought.

like image 112
Valdrinium Avatar answered Sep 28 '22 10:09

Valdrinium