How to prevent a constructor from creating argument types in C ++?

I have a class for storing unsigned large numbers, and I would like to allow the user to create objects using types such as long long , int , unsigned int , etc., as well as from string . I created the constructor BigNumber(const unsigned long long) and BigNumber(const std::string) , but I want to prevent the user from using constructs such as: BigNumber('a') or BigNumber(true) . I heard about explicit , so I decided to write the following lines in my class definition:

explicit BigNumber(const bool) = delete; explicit BigNumber(const char) = delete;

Unfortunately, when I want to create an object like: BigNumber x(1) or BigNumber("1234") , I get an error that the call to the overloaded constructor is ambiguous. I did not receive this message before I wrote these lines using explicit . How to solve this problem?

+5
source share
5 answers

This can be done using the template:

 template<typename T, typename = typename std::enable_if< std::is_integral<T>::value && !std::is_same<char, T>::value && !std::is_same<bool, T>::value>::type> explicit BigNumber(T const n); 

This constructor cannot be called with bool or char s.

+4
source

Unfortunately, you cannot do this.

First let me explain the obvious.

Suppose we have:

 class Y; class X { X(const Y&); }; void Z(const X&); 

This tells the compiler "given Y , we can make X ", so the code looks like this:

 Y y; Z(y); 

It works, the compiler does this:

 Y y; Z(X(y)); /* temporary X constructed here from the y*/ 

An explicit ban on this.

So, if you have:

 class X { explicit X(const Y&); }; 

Then

 Z(y); 

Will not work! Since you must explicit construct the X construct from Y

This is called a conversion constructor, the idea is that you can freely convert. This is great when you have something that expects string and give it a const char* , since it implicitly builds the string and passes it, you are β€œsuffering” from the interaction of the main types.

+2
source

You can use a similar trick, as when creating classes that are not copyable.

 struct A { explicit A(int) { ... } explicit A(std::string) { ... } // prevent any other types template <typename T> A(T) = delete; }; 

This will require that the argument to constructor A be exactly int or string .

 A a1{1}; // OK A a2{std::string("foobar")}; // OK A a3(1L); // Error: use of deleted function 'A::A(T) [with T = long int]' 
+1
source

First of all, explicit is not what you need. Explicit simply means that the constructor will not be used as the conversion operator, but it will not stop you from using the constructor in constructive statements.

The problem you are facing is that 1 is not bool , long long int or char - but can be converted to any of them, the compiler does not know which one. You will need to add the deleted constructor for each base type (a tedious task, really) if you really want to block certain types of integrals.

0
source

Not applicable to explicit .

BigNumber("1234") can use BigNumber(const std::string) or BigNumber(const bool) (the latter should be preferred btw).

Therefore, do not provide a constructor for bool (or provide a constructor also for const char* (exact match)).

0
source

Source: https://habr.com/ru/post/1236631/


All Articles