Can I prevent implicit conversion in parameter to assignment operator - c++

I am implementing a type (TParameter) which shall be both a boolean (to indicate if the value is valid) and a data value of arbitrary type.
The idea is that if a method takes a parameter of some type, then I can set it to false, to indicate that the value is invalid.
Like this:
someVariable = 123; // use the value 123
someVariable = false; // mark variable as invalid/to-be-ignored
A simplified version of my code:
template <class T>
class TParameter
{
public:
TParameter()
: m_value(),
m_valid(false)
{}
// assignment operators
TParameter& operator= (const T& value)
{
m_value = value;
m_valid = true;
return *this;
}
TParameter& operator= (bool valid)
{
m_valid = valid;
return *this;
}
private:
T m_value;
bool m_valid;
};
void test()
{
TParameter<int16_t> param;
param = false;
param = int16_t(123);
param = 123;
}
When compiling the code I get the error:
ambiguous overload for ‘operator=’ (operand types are ‘TParameter<short int>’ and ‘int’)
The problem is that integer values can be implicitly cast to a bool, and therefore the last line in test() does not compile.
Is it possible to tell the compiler that TParameter& operator= (bool valid) shall only be used if the parameter is a bool (i.e. disable implicit cast to bool)?

You can make the 1st overload template, then the 2nd overload will be preferred only when being passed a bool (because non-template function is preferred over template under same situation). Otherwise, the template version will be selected because it's an exact match.
template <typename X>
TParameter& operator= (const X& value)
{
m_value = value;
m_valid = true;
return *this;
}
TParameter& operator= (bool valid)
{
m_valid = valid;
return *this;
}
LIVE
BTW: In your code, implicit conversion happens when the operator= being called; int is converted to int16_t and then passed to operator=. In the code above, the implicit conversion happens inside the operator=, i.e. m_value = value;.

Related

Operator overloading with template question

I'm unable to recognize the following forms of operator overloading, specifically with the template parameter involved. I found this while reading an article on nullptr. I do not see these forms on cppreference overloading page either.
Can anyone explain these forms of overloading and what they are doing?
Thanks
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
Starting simpler:
struct A {
operator int() {return 3;}
};
void function() {
A aobject;
int value = aobject; //uses A::operator int()
//value is now 3
}
operator int is a curious member function that allows the struct to be converted to an int. It's very curious in that it's the only case in C++ that uses the return type in order to resolve which overloaded function to call, including that it can resolve template types.
struct A {
operator int*() {return 0;}
};
void function() {
A aobject;
int* value = aobject; //uses A::operator int()
//value now holds the value 0 (NULL)
}
This is the same thing, but now A can be converted to an int*. It is otherwise self explanatory.
struct A {
template<class T>
operator T*() { return 0; }
};
void function() {
A aobject;
short* value = aobject; //uses A::operator T*<int>()
//value now holds the value 0 (NULL)
}
By making A::operator T*() a template method, we can make our class able to be converted to a pointer to any type. This expands the options for what you can convert to. operator T C::*() { return 0; } is similar, but also allows conversion to pointers to any member of any class, which is very rare and advanced.

Understanding C++ choice of conversion operator

I've got an optional-like class (I can't use optional since it's in C++17). It contains a (possible) value along with a flag indicating if it's valid. I've got an explicit bool operator and a conversion operator to get the value out. The problem is, sometimes C++ will choose the bool operator in an explicitly bool context (an if statement), and other times it won't. Can anyone help me understand this behavior:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() {
return std::move(value());
}
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1) {
double p = m1;
(void)p;
}
}
Whenever you write:
if (x)
That is equivalent to having written:
bool __flag(x);
if (__flag)
This is called a contextual conversion to bool (note that it's direct-initialization, so the explicit conversion function is a candidate).
When we do overload resolution on that construction for m0, there's only one valid candidate: explicit operator bool() const.
But when we do overload resolution on that construction for m1, there are two: explicit operator bool() const and operator double&&(), because double is convertible to bool. The latter is a better match because of the extra const qualification on the bool conversion function, even though we have to do an extra double conversion. Hence, it wins.
Would simply remove operator T&&() from your interface, as it doesn't make much sense for this type.
As soon as T is convertible to bool (double is, std::pair is not) your two operators will match and you'll get an ambiguous call, or one may be a better match for some reason.
You should only provide one of the two operators, not both.
Your operator bool and conversion operator are ambiguous in this design.
In the first context, std::pair<int,int> does not cast to bool
so the explicit bool conversion operator is used.
In the second context, double does cast to bool so the T
conversion operator is used, which returns a double, which then
casts to bool implicitly.
Note in the second context, you are calling std::move which puts value in a valid but undefined state, which leads to undefined behavior when you cast value to double a second time in the if block.
I'd use a named member function to indicate if it is valid, and modify the conversion operator:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() && { return std::move(value_); } // if rvalue
operator T&() & { return value_; } // if lvalue
operator const T&() const & { return value_; } // if const lvalue
bool valid() const { return valid_; }
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0.valid()) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1.valid()) {
double p = m1;
(void)p;
}
}
EDIT: The conversion operator should only move from member value_ if the maybe object is an rvalue reference. Using && after a functions signature specializes for this case -- please see Kerrek SB's answer for more information.

Uninitialized std::string within a class?

I am a bit perplexed. I am creating a custom "Variant" class, but am running into a strange issue with std::string member.
When I try to assign it from another Variant instance, I get an exception that the string is a bad pointer. I am not sure I understand why this would happen because the string should be an instance when the class is constructed.
Here is my code:
class Variant : ObjectBase {
public:
Variant() {};
Variant(const Value& node) { parse(node); };
ValueType Type = ValueType::Unknown;
Variant& operator= (bool value)
{ Type = ValueType::Boolean; value_.vBool = value; return *this; };
Variant& operator= (std::string value)
{ Type = ValueType::String; value_string_ = value; return *this; };
Variant& operator= (double value)
{ Type = ValueType::Double; value_.vDouble = value; return *this; };
Variant& operator= (int32_t value)
{ Type = ValueType::Int32; value_.vInt32 = value; return *this; };
Variant& operator= (int64_t value)
{ Type = ValueType::Int64; value_.vInt64 = value; return *this; };
Variant& operator= (uint32_t value)
{ Type = ValueType::Uint32; value_.vUint32 = value; return *this; };
Variant& operator= (uint64_t value)
{ Type = ValueType::Uint64; value_.vUint64 = value; return *this; };
Variant& operator= (const Value& node) { parse(node); };
private:
union Any {
int32_t vInt32;
int64_t vInt64;
double vDouble;
bool vBool;
uint32_t vUint32;
uint64_t vUint64;
Any() : vDouble(0) {};
} value_;
std::string value_string_;
};
The exception occurs when I do this:
Variant v1 = "Hello";
Variant v2 = v1; // <--- Here is where it occurs
Now, if I create a manual = operator overload, I can see that v2 value is a bad pointer, but I am not sure why. Here is my overload:
Variant& operator= (const Variant value) {
value_ = value.value_;
value_string_ = value.value_string_;
Type = value.Type;
return *this;
}
Maybe I am tired or something, but it is not obvious to me what am I missing here.
Assuming this code compiles at all (which is not clear, as complete code has not been given) then the statements
Variant v1 = "Hello";
Variant v2 = v1;
invoke constructors, not assignment operators.
In the first, since Variant does not supply a constructor that accepts a string, this means Value does, and an implicit conversion to Value is being done before invoking the constructor for Variant. The problem is therefore either in the constructor for Value, or in working of the parse()` function. Neither of those have been shown though.
The second statement invokes the compiler-generated copy constructor. Which, if the preceding construction has not worked as required, will also introduce problems.

C++ Templates and operator overloading

I am learning templates and operator overloading. I have written some code but I am confused... Let me explain...
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType():locked(false)
{
value = 0;
}
T operator=(T val)
{
value=val;
return value;
}
tempType<T> operator=(tempType<T> &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
And I did...
int main(void)
{
tempType<int> i;
tempType<bool> b;
tempType<float> f;
i.value = 10;
i = i + f;
return 0;
}
What code I need to write in order to execute
tempType<T> operator=(tempType<T> &val){}
Also, I why operator T() is required?
Unless you implement move semantics, operator= should always take a const & reference to the source value. It should also return a reference to the modified object.
tempType & operator=(T const & val)
{
value=val;
return * this;
}
operator T is an implicit conversion function which allows any tempType object to be treated as an object of its underlying type T. Be careful when specifying implicit conversions that they won't conflict with each other.
An implicit conversion function usually shouldn't make a copy, so you probably want
operator T & ()
{
return value;
}
operator T const & () const
{
return value;
}
Given these, you shouldn't need another overload of operator = because the first overload will simply be adapted by the conversion function to a call such as i = b;.
If a series of conversions will result in the operator=(T const & val) being called, you should avoid also defining operator=(tempType const & val) because the overloads will compete on the basis of which conversion sequence is "better," which can result in a brittle (finicky) interface that may refuse to do seemingly reasonable things.
I think I know all the answers so I might as well post full response.
To override default operator= you should declare it as tempType<T> operator=(const tempType<T> &val){}. Now you need to call the method explicitly via i.operator=(other_i).
If you correct the declaration you can use it like this:
tempType<int> i;
tempType<int> other_i;
i = other_i; // this is what you just defined
The operator T() is called a conversion operator. It is kind of reverse or counter part of the conversion constructor which in your case would be tempType(const &T value).
It is used to convert a class object into a given type. So in your case you would be able to write:
tempType<int> i;
int some_int;
some_int = i; // tempType<int> gets converted into int via `operator int()`
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType() : value(), locked(false)
{
value = 0;
}
//althought legal, returning something different from tempType&
//from an operator= is bad practice
T operator=(T val)
{
value=val;
return value;
}
tempType& operator=(const tempType &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
int main(void)
{
tempType<int> a;
tempType<int> b;
a = b;
return 0;
}
in the code, a = b calls the operator.
As for the second question, the operator T() is not needed "by default". In your example, it is used when you write i+f:
i is converted to an int
f is converted to a float
the operation (+) is performed
T tempType<int>::operator=(T val) is called for the assignement

Template cast operator C++

I am trying to make a little wrapper class such as
template <typename T>
class EdgeTriggeredState
{
public:
void Tick()
{
oldData = newData;
}
EdgeTriggeredState& operator =(const T& v)
{
newData = v;
return *this;
}
// T& operator = (void)
// {
// return oldData;
// }
// T& operator T()
// {
// return oldData;
// }
private:
T oldData;
T newData;
};
Basically I want to be able to directly assign to a variable of type T the value wrapped by the class. I have tried implementing both an assignment (to type T) operator and a cast operator to type T. I am a bit rusty on my C++ as I have been working solely in C. Is there a way to go about implementing this without creating a named getter method?
When I uncomment the first implementation attempt I get error
"../EdgeTriggeredState.h:19:21: error: ‘T& EdgeTriggeredState::operator=()’ must take exactly one argument"
When I uncomment the second implementation (and comment out the first) I get error:
"../EdgeTriggeredState.h:24:16: error: return type specified for ‘operator T’"
When you write an operator T, the return type is implicit, so your code should look something like:
template <typename T>
class DumbWrapper {
T oldData;
T newData;
public:
DumbWrapper& operator = (const T& val) {
newData = val;
return *this;
}
operator T() {
return oldData;
}
};
[Also note the semicolon at the end, and the fact that the constructor and conversion operator were probably intended to be public.]