c++implicit-conversioncopy-initialization

Can't copy initialize string literals for my custom datatype (class object) even when I have the constructor for that


I have a program which lets C++ support arbitrary precision numbers (very large numbers). Takes datatype as string or string literals. Basically "762...."

Compiler: g++ (Debian 10.2.1-6) 10.2.1 20210110

I have class big_int, and it handles all the things. I also have operator overloads that cleanly handles the arithmetic and assignments.

There is one problem, I can't copy initialize string literals for big_int objects

Rough definition of my class:

class big_int {
  // operator overloads
  // Constructors
  
  big_int (){}
  big_int (const big_int& other){ *this = other;} // has the =overload for direct copying
  
  explicit big_int (const char* s){ value = s;}
  big_int (const std::string& s){ value = s;}
}
int main(){

big_int num2 = "99"; // doesn't work

}
``

> error:conversion from ‘const char [3]’ to non-scalar type ‘big_int’ requested big_int num2 = "99";

Even tough this works

```cpp
int main(){

  big_int str_num("45569");

}

I have the following operator overloads (I don't think this info is necessary, but still):

class big_int {

public:
  
  // Member Operator Overloads  -> ->
  
  // assingment overloads
   
    // std::string 
  big_int& operator=(const std::string& other) { // = overload (strings)
    this->value = other; // no self assingment
    return *this; // de-referenced object
  }
    // big_int
  big_int& operator=(const big_int& other) { // = overload (big_int)
    if (this != &other) this->value = other.value; // no self assingment
    return *this; // de-referenced object 
  }
  // --------------------------
  
  
  // plus overloads
  
    // std::string
  big_int operator+(const std::string& other) const{ // + overload (strings)
    return big_int(arb_add(value, other));
  }
    // big_int
  big_int operator+(const big_int& other) const{ // + overload
    return big_int(arb_add(value, other.value));
  }
  
  
  // -----------------------
  
  // minus overloads
  
    // std::string
  big_int operator-(const std::string& other) const{ // - overload
    return big_int(arb_sub(value, other));
  }
  
    // big_int
  big_int operator-(const big_int& other) const{ // - overload
    return big_int(arb_sub(value, other.value));
  }
  
  // -------------------------------
  
}

// GENERAL OPERATOR OVERLOADS
std::ostream& operator<<(std::ostream& os, const big_int& other) {
  os << other.value;
  return os;
}
// ========================

I am trying to copy initialize my custom datatype with string literals, expecting this:

int main() {

big_int num2 = "99"; // copy initialization

std::cout << num2 << std::endl; //should print 99 (direct printing of type possible because of << overload)

}

But g++ gave this error

main.cpp: In function ‘int main()’:
main.cpp:300:18: error: conversion from ‘const char [3]’ to non-scalar type ‘big_int’ requested
  300 |   big_int num2 = "99"; // error: conversion from ‘const char [3]’ to non-scalar type ‘big_int’ requested big_int num2 = "99"
      |                  ^~~~

Even tough this works

int main() {

  big_int str_num("45569");

}

The problem is that I have the required constructors for it work, but it completely denies to do that.

When the 'explicit' is removed, it causes ambiguous overloads with operator= and all member operator overloads.


Solution

  • "99" is a c-string literal, it has the the type const char[3]. When you do

    big_int num2 = "99";
    

    since the types do not match the compiler tries to convert "99" to something that can be used to initialize a big_int.

    It can't use

    explicit big_int (const char* s)
    

    because the constructor is marked as explicit.

    It also can't use

    big_int& operator=(const std::string& other)
    

    because even though there is an = used, we are initializing, not assigning so the assignment operators can't be used.

    This means there is no implicit way to convert "99" into something that can initialize a big_int. You either need to explicitly do it, or remove the explicit from the constructor.

    Common guidance is to not remove the explicit as non explicit converting constructors can lead to some unexpected behavior.