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.
Related
Say I have the following struct:
struct Parameter {
double value;
double error;
};
So that I'm usually working with vectors of that struct (ie. std::vector<Parameter>), and ocasionally I want to set a vector of values (but not errors) in that vector of parameters by using the operator= with a standard std::vector, for convenience.
std::vector<Parameter> vector_of_parameters;
std::vector<double> vector_of values;
....
vector_of_parameters = vector_of_values;
To do so, I'm trying to overload operator= for this struct as follows:
std::vector<Parameter> operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
But this will return an error saying that std::vector operator=(const std::vector& v) must be a non-static member. So if I understand it correctly, I have to define this as a member function of the operator as:
std::vector<Parameter>::operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
The error now says that a syntaxis with template<>, but I dont really see it, or understand it, and don't know what more can I do.
You cannot overload the assignment operator of std::vector. operator = must be a member function and you just can't add a member function to std::vector.
What you can do is make a convenience function like create_parameters that takes a std::vector<double> and returns a std::vector<Parameter>. That would look like
std::vector<Parameter> create_parameters(std::vector<double> const& params)
{
std::vector<Parameter> ret(params.size());
std::transform(params.begin(), params.end(), ret.begin(),
[](auto value) { return Parameter{value, 0}; });
return ret;
}
and then
vector_of_parameters = vector_of_values;
would become
vector_of_parameters = create_parameters(vector_of_values);
I think that an alternative simple way to create std::vector<Parameter> from std::vector<double> is defining a single argument constructor Parameter(double value) which accepts the Parameter::value:
#include <vector>
#include <optional>
struct Parameter
{
double value;
std::optional<double> error;
explicit Parameter(double v) : value(v)
{}
};
Then you can use range-constructor as follows:
DEMO
std::vector<Parameter> v_of_parameters(v_of_values.cbegin(), v_of_values.cend());
So in the interest of creating a Minimal. Complete, Verifiable Example I have created a toy iterator here (I know it's not perfect, it's just for the purposes of asking a question):
class foo : public iterator<input_iterator_tag, string> {
string _foo;
static const size_t _size = 13;
public:
const string& operator*() { return _foo; }
const foo& operator++() {
_foo += '*';
return *this;
}
const foo operator++(int) {
auto result = *this;
_foo += '*';
return result;
}
bool operator==(const foo& rhs) { return _foo.empty() != rhs._foo.empty() && _foo.size() % _size == rhs._foo.size() % _size; }
bool operator!=(const foo& rhs) { return !operator==(rhs); }
};
I read that an InputIterator needs to have defined the Member Selection Operator. The Indirection Operator makes sense, but a Member Selection Operator is confusing to me here. How would an Member Selection Operator be implemented for foo?
const string* operator->() const { return &_foo; }
Example usage:
foo i;
++i;
assert(i->length() == 1);
The way this works is that the compiler will generate repeated calls to operator-> until the return type is a raw pointer (so in this case just one call to foo::operator->), then do the regular member selection operation on that pointer.
The operator->() should return a pointer type of the type the container holds that the iterator is used on. So if you have a container that holds a std::string then the iterator::operator-> should return a std::sting*. In your case since you derive from std::iterator you can use the pointer typedef for the return type.
I'm trying to get a firm grasp on move and copy constructors and assignments.
I have the following simple templated class:
template<typename T>
class Box {
public:
// Copy constructor
explicit Box(const T& val) {
value = val;
}
// Move constructor
explicit Box(const T&& val) {
value = val;
}
// Set and get value
void set(T val) { value = val; }
T get() { return value; }
// Copy assignment operator
T& operator=(const T& val) {
value = val;
}
// Move assignment operator
T& operator=(const T&& val) {
value = val;
}
private:
T value;
};
Which is used by the following code:
int main() {
Box<int> iBox{ 1 };
int j = 5;
Box<int> jBox{ j };
iBox = jBox;
return 0;
}
I would expect, if my code is correct, that when I step into iBox = jBox, I should step into one of the assignment overloads. Why is this not happening?
You have not declared an assignment operator taking a right-hand operand of type Box, so the compiler has no other option than to use its generated default assignment operator.
You also have no return statements in your assignment operators, by the way.
Maybe you meant to write:
Box& operator=(const Box& other) {
value = other.value;
return *this;
}
Box& operator=(Box&& val) {
value = other.value;
return *this;
}
Notice that an rvalue reference to const would make little sense.
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
The D Programming Language Version 2 has a nifty method to overload an expression like this:
classInstance[someName] = someValue;
Or as D Function defined in this little example:
ref Map opIndexAssign(ref const(ValueT) value, ref const(NameT) name)
{
this.insert(name, value);
return this;
}
Is this possible in C++ (ideally without using the STL)? If so, how?
Normally, you would use a proxy object as the return type of operator[]; that object will have a custom operator= defined. The vector<bool> specialization in the C++ Standard Library uses a proxy to get the behavior you are looking for. The proxy-based solution isn't as transparent as the D version, though. The code is something like:
class proxy;
class my_map {
public:
proxy operator[](const key_type& k);
// Rest of class
};
class proxy {
my_map& m;
key_type k;
friend class my_map;
proxy(my_map& m, const key_type& k): m(m), k(k) {}
public:
operator value_type() const {return m.read(k);}
proxy& operator=(const value_type& v) {m.write(k, v); return *this;}
};
proxy my_map::operator[](const key_type& k) {
return proxy(*this, k);
}
Not being familiar with D, I'm going to have to assume that your first code snippet ends up calling opIndexAssign with value = someValue and name = someName?
If so, it can be done in C++, but not in such a simple manner. You could overload the [] operator, and return a proxy object with a custom = operator as follows (very basic, contrived example):
class MyProxy
{
public:
MyProxy (int& ref) : valueRef(ref) { }
MyProxy& operator = (int value) { valueRef = value; return *this; }
private:
int& valueRef;
};
class MyClass
{
public:
MyProxy operator [] (std::string name);
private:
int myVal;
};
MyProxy& MyClass::operator [] (std::string name)
{
if (name.compare("myVal"))
return MyProxy(myVal);
...
}
int main ( )
{
MyClass mc;
mc["myVal"] = 10; // Sets mc.myVal to 10
}
I'd like to strongly stress that the above is not very pretty/well formed code, just an illustration. It has not been tested.
EDIT: Too quick for me Jeremiah!!!