How can I prevent implicit conversion from char to int? - c++

I have the following code:
#include <iostream>
template<typename T> class DynArray
{
T *contents;
int size;
public:
explicit DynArray(int initial_size);
};
int main()
{
DynArray<std::string> b('7');
return 0;
}
My question is: how can I prevent the implicit conversion from char to int from compiling? (i.e. this line: `DynArray b('7');

You can't directly, but you can make an overload of the constructor which gets chosen first when passed a char...
explicit DynArray(char);
Make it private and don't define it, just declare it. The same as declaring but not defining a copy ctor/copy assignment operator to prevent a class from being copyable.
Or, with C++11, make it deleted (which is the new cleaner/clearer/better way of doing the above)...
explicit DynArray(char) = delete;

Related

Objects that can be initialized but not assigned

I need to create a class whose objects can be initialized but not assigned.
I thought maybe I could do this by not defining the assignment operator, but the compiler uses the constructor to do the assignment.
I need it to be this way:
Object a=1; // OK
a=1; // Error
How can I do it?
Making a const will do the trick
const Object a=1; // OK
Now you won't be able to assign any value to a as a is declared as const. Note that if you declare a as const, it is necessary to initialize a at the time of declaration.
Once you have declared a as const and also initialized it, you won't be able to assign any other value to a
a=1; //error
You can delete the assignment operator:
#include <iostream>
using namespace std;
struct Object
{
Object(int) {}
Object& operator=(int) = delete;
};
int main()
{
Object a=1; // OK
a=1; // Error
}
Alternative Solution
You can use the explicit keyword:
#include <iostream>
using namespace std;
struct Object
{
explicit Object(int) {}
};
int main()
{
Object a(1); // OK - Uses explicit constructor
a=1; // Error
}
Update
As mentioned by user2079303 in the comments:
It might be worth mentioning that the alternative solution does not prevent regular copy/move assignment like a=Object(1)
This can be avoided by using: Object& operator=(const Object&) = delete;
I hoped this would be so by not defining the assignment operator
This doesn't work because the copy assignment operator (which takes const Object& as parameter) is implicitly generated. And when you write a = 1, the generated copy assignment operator will be tried to invoke, and 1 could be implicitly converted to Object via converting constructor Object::Object(int); then a = 1; works fine.
You can declare the assignment operator taking int as deleted (since C++11) explicitly; which will be selected prior to the copy assignment operator in overload resolution.
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
e.g.
struct Object {
Object(int) {}
Object& operator=(int) = delete;
};
There're also some other solutions with side effects. You can declare Object::Object(int) as explicit to prohibit the implicit conversion from int to Object and then make a = 1 fail. But note this will make Object a = 1; fail too because copy initialization doesn't consider explicit constructor. Or you can mark the copy assignment operator deleted too, but this will make the assignment between Objects fail too.
How can I do it?
Option 1:
Make the constructor explicit
struct Object
{
explicit Object(int in) {}
};
Option 2:
delete the assignment operator.
struct Object
{
Object(int in) {}
Object& operator=(int in) = delete;
};
You can use both of the above options.
struct Object
{
explicit Object(int in) {}
Object& operator=(int in) = delete;
};
Option 3:
If you don't want any assignment after initialization, you can delete the assignment operator with Object as argument type.
struct Object
{
explicit Object(int in) {}
Object& operator=(Object const& in) = delete;
};
That will prevent use of:
Object a(1);
a = Object(2); // Error
a = 2; // Error
Deleted functions are available only from C++11 onwards, for older compilers you can make the assignment operator private.
struct Object
{
Object(int) {}
private:
Object& operator=(int);
};
Compiler will now throw error for
Object a=1; //ok
a=2; // error
But you can still do
Object a=1,b=2;
b=a;
Because the default assignment operator is not prevented from being generated by the compiler. So marking default assignment private will solve this issue.
struct Object
{
Object(int) {}
private:
Object& operator=(Object&);
};

Template and using (#define) in class constructor

I've implemented stack process.this program is supposed to work exactly the same as a real stack memory.moreover i'm trying to use Template and make the the program more generic. I've got a problem in using #define DEFAULT_SIZE 10 as the argument of class constructor.
First of all when i put DEFAULT_SIZE in the prototype of the constructor it goes smoothly:
#define DEFAULT_SIZE 10
template<typename T>
class stack {
public:
stack(int size=DEFAULT_SIZE);
private:
T *elements;
int size;
int count;
};
template<typename T>
stack<T>::stack(int s) {
cout << "--constructor called\n";
size = s;
elements = new T[size];
count = 0;
}
But when I just put DEFAULT_SIZE in outline definition of the class constructor i get this error: no appropriate default constructor available
#define DEFAULT_SIZE 10
template<typename T>
class stack {
public:
stack(int size);
private:
T *elements;
int size;
int count;
};
template<typename T>
stack<T>::stack(int s=DEFAULT_SIZE) {
cout << "--constructor called\n";
size = s;
elements = new T[size];
count = 0;
}
Finally the main of the program:
int main() {
stack<int> u;
u.push(4);
}
My question is not about "Why can templates only be implemented in the header file?" My problem is the place where I use DEFAULT_SIZE.
I suppose, the problem is just in difference of template declaration:
stack(int size);
and template definition:
stack<T>::stack(int s=DEFAULT_SIZE) {
...
}
Default values must be in declaration part, and if method signature in definition is different from declaration (you add DEFAULT_SIZE in definition) compiler is not sure you write the same constructor. Note, DEFAULT_SIZE is applied when s value not given to constructor, so you definition will work as default constructor, but declaration is constructor with one parameter.
It is mentioned in C++ specs(ยง8.3.6 pt.4) that
For non-template functions, default arguments can be added in later declarations of a function in the same scope.
So you can't assign the default value in the definition. That is the reason for not working of second approach.
While first approach will work as it is a desired behavior that you can omit the default values in the definition.
If you compile your second code snippet with Ideone for example it gives you "redeclaration of 'stack::stack(int)' may not have default arguments" (see http://ideone.com/UKIx2r).
prog.cpp:16:35: error: redeclaration of 'stack<T>::stack(int)' may not have default arguments [-fpermissive]
stack<T>::stack(int s=DEFAULT_SIZE) {
Default parameters have to be specified in the first declaration
If you declare your own constructors the default constructor will be deleted. However, your constructor will act as a default constructor as long as all parameters have default values.
Your first part declares a correct default value for the constructors parameter. Your second part does not and your compiler has no chance to use the constructor as a default constructor.

Templated class constructor uses wrong overload when in struct

The test is as following:
class NotInit{};
NotInit NOT_INIT;
template<class T>
class Optional{
T value;
bool has_value;
public:
Optional() : value(), has_value(false){}
explicit Optional(NotInit):value(), has_value(false) {}
explicit Optional(T const & val):value(val), has_value(true){}
explicit Optional(Optional<T> const & other) {}
Optional& operator=(Optional<T> const & other){}
Optional& operator=(T const & other){}
};
enum X {
FIRST
};
struct Some {
Optional<X> member;
};
int main(int, char**){
Optional<X> const opt(NOT_INIT);
Some s = {NOT_INIT};
return 0;
}
Clang 3.4 complains:
../st.cpp:31:12: error: no viable conversion from 'NotInit' to 'Optional<X>'
Some s = {NOT_INIT};
^~~~~~~~
1 error generated.
Why does it complain for the struct initialization but ont for the varaiable? Why is it not choosing the constructor with the proper parameter?
What overload is missing so that i can use this to init the struct?
I cannot use boost and i'm not sure this error would not appear if using boost::optional.
You are marking your constructors explicit. Thus when you are trying to initialize your struct Some with a brace-initializer list, you are triggering an implicit conversion...
This is prevented here:
class NotInit;
template <typename T>
class Optional {
// Here => Optional(NotInit) cannot be called implicitly
explicit Optional(NotInit):value(), has_value(false) {}
};
/* ... */
Some s = {NOT_INIT}; // Implicit call to Optional<X>(NotInit): whoops!
If you remove the explicit, you can keep:
Some s = {NOT_INIT};
If you chose not to, then you'll have to instantiate s like this:
Some s = {Optional<X>(NOT_INIT)};
In any case, you'll have to remove the explicit keyword on your copy constructor (which has to perform a copy and not be a "dummy" constructor).
Why? Because once the Optional<X> object is built from NOT_INIT, a copy has to be made to s.member.
Note: You could also provide a move constructor, which would be more appropriate in that case, since the object you get from the conversion of NOT_INITis an rvalue.

"Explicit" preventing automatic type conversion? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does the explicit keyword in C++ mean?
I do not understand the following. If I have:
class Stack{
explicit Stack(int size);
}
without the keyword explicit I would be allowed to do:
Stack s;
s = 40;
Why would I be allowed to do the above if explicit wasn't provided?? Is it because this is stack-allocation (no constructor) and C++ allows anything to be assigned to the variable unless explicit is used?
This line
s = 40;
is equivalent to
s.operator = (40);
Which tries to match the default operator = (const Stack &). If the Stack constructor is not explicit, then the following conversion is tried and succeeds:
s.operator = (Stack(40));
If the constructor is explicit then this conversion is not tried and the overload resolution fails.
hey its pretty simple .
the explicit key word only stops complier from automatic conversion of any data type to the user defined one.. it is usually used with constructor having single argument .
so in this case u are jus stopping the complier from explicit conversion
#include iostream
using namespace std;
class A
{
private:
int x;
public:
A(int a):x(a)
{}
}
int main()
{
A b=10; // this syntax can work and it will automatically add this 10 inside the
// constructor
return 0;
}
but here
class A
{
private:
int x;
public:
explicit A(int a):x(a)
{}
}
int main()
{
A b=10; // this syntax will not work here and a syntax error
return 0;
}

How do I create a class that can initialize C++ data types?

The title basically says it all. I mainly want to do this so that I can create an object (say, a custom string object) that can initialize the parameters of other functions in other APIs. Here's an example of me trying to get a custom integer class to work:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
friend int &operator=(int &i, test t);
};
int &operator=(int &i, test t)
{
return (i = t.member);
}
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
Unfortunately I receive an error saying that operator= needs to be a member function. I understand the C++ standard's goal in preventing static and non-member overloads for the assignment operator from being implemented, but is there any other way to do this? Thanks for any help/suggestions!
This is not done with an assignment operator but with an overloaded typecast. This would make your main function work like expected:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() const {return member;}
};
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
What you are trying to do needs an conversion operator
operator int()
{
return this->member;
}
For the class you are trying to write(containing only integer members), You do not need to overload the = operator.
= operator is one of the member functions that is generated by the compiler by default for every class. Caveat is, it does a simple bit by bit copy(shallow copy) of class members, since you have only integers it should be good enough for you.
You would need to overload the = operator if you had dynamically allocated pointers as member functions, because in that case a shallow copy of those pointers would result in all the objects containing a member pointer pointing to the same dynamic memory location & if one of the object finishes it lifetime, other objects are left with a dangling pointer.
As #Tony, aptly points in out comments Shallow copy is usually bad but not always. See his comments for a scenario.
If at all you want to overload the assignment operator check out the Copy and Swap Idiom to do it right way.
You should also check out the Rule of Three.
Try this:
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() {return this->member;}
};
int main(void)
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
The assignment operator cannot be a friend function. The assignment operator can only be declared as a non-static member function. This is to ensure that it receives the L-value as its first operand. The same is true for the [], (), and -> operators. In your case, since int is an build-in type, you cannot use member function. You can implement operator int() to cast your user-defined type to int.