C++ Template Assignment Operator Overloading
Overloaded assignment operator for class template objects
Here is the class definition:
And here is the definition of the overloaded assignment operator:
And here is my main function that tests the operations on objects of the class:
The problem I'm having starts from where the assignment operator is being tested: for double and string datatypes, the upper input/output section works fine, but the assignment section freezes the display until the program execution is manually terminated!!
Any helpful insights would be highly appreciated. Please feel free to ask or request for clarifying info about the code. Thanks as I eagerly await your responses.
Also problem with negative elements. Remember: arratPtr[i] will not use overloaded operator[].
If it works for ints, I think you have some problems with operator[] too. Show it.
You wrote: "What if lower bound is 9 and upper bound is 100? You will create an array of size 1, but will try to access 99th element."
Did you mean to write 10 and not 100? 'Cause that's the only way the array size would result in 1.
Also you wrote: "Also problem with negative elements." Did you mean negative elements of the array OR negative parameters at object declaration? Anyways, the index of arrayPtr (= i) can never be negative, given some object declaration constraint (e.g. upperBound>lowerBound) and the way I wrote the constructor.
The constructor is definitely not the problem; like I implied in my earlier post, the objects of the class were successfully created (for integer, double, and string datatypes); it's the assignment operation that's failing me, I'm guessing. Here are my constructor and the overloaded operator[] you asked for:
By the way, I'm jealous of the way you captured the exact fonts/color of my program segment. Yours even had line numbers! So cool! How do I go about doing that on here; I'm fairly new to the forum. Thanks.
Did you mean to write 10 and not 100? |
In your constructor: if you pass (-10, 0) as parameters, it will create array of size 10 with valid indexes [0,9]
But if you call yoir object as x[-8] it will chech that it is between lower and upper bound (which is true) and will try to do , which is dangerous and can lead to crash.
You probably intended to normalize your range and access underlying array as
As for me I would make arrayUpperBound acceptable to specify as an index.
I wouldn't use copy, I'd use move instead |
Logically that wouldn't make sense. This is copy assignment, not move assignment. The elements to be moved are const and thus cannot be "moved" as doing so requires the source elements to be modified.
[edit: typo'd]
@MiiniPaa: I see your point about the dangers of problematic "extreme" parameters during object declaration. However, the point still remains that I did not use those extreme parameter cases in my first post above that worked for integer datatypes but failed for double/string datatypes. I have already designed a more versatile constructor to take care of those extreme parameters at object declarations. It just boggles the mind that the same set of parameters that worked for integers would not work for double/string datatypes.
@vlad from moscow: Let's assume the "const" in the return type were the problem, how come this did not affect the integer datatype runs I did, all of whose assignment operations were successfully implemented??
@xerzi: Well, the "Law of the Big Three" stipulates inclusion of the copy constructor in a class with pointer data members.
@cire: I think I agree with your objection to xerzi's post.
Still looking forward to any helpful insights. Thank you all.
I did not know what is your problem with the copy assignment operator because you did not say nothing about it. |
If you examine my first post in which I show function main, you would see four assignment statements in lines 40, 45, 50 & 55. These are the lines that are giving me a headache. Like I said in my first post, these assignment statements are successfully achieved when the objects are of integer datatype. However, program execution halts if the objects are of double/string datatypes. That's why it makes sense to think that my overloaded assignment statement is the problem. But from a theoretical standpoint, I can't see where my overloaded assignment operator function is wrong.
@cire: I'll try to go over that website you suggested.
Thanks all! The search for a reason for this datatype-based execution anomaly continues.
You wrote:
I would guess that, since every function you've shown so far has handled indexing your array incorrectly (ie: not adjusting the index to be zero-based before using it to access the array), ... |
Well that's the point of the task I embarked upon: to create non-conventional (not necessarily zero-based) arrays that are strictly based on the definition of the class to which they belong. I beg to differ that the indexing is being handled incorrectly because my overloaded array index operator function(definition shown in an earlier post) has already taken care of the non-zero-based feature of the objects of the class.
If you create it will create array with one element.
If you do it will pass your checks, but will access 100th one which is overflow
If you create it will create array with ten elements.
If you do it will pass your checks, but will access -8th element, which is invalid for arrays.
Look what your function doing in these cases step by step and you will probably understand that yourself
I think I'm starting to see your points about the array indexing angle to my problem. For some reason, I have apparently and erroneously overlooked the fact that within the function definition of the operator I'm trying to overload, the primary ANSI C++ definition of that same operator is still preserved.
MiiNiPaa's examples sort of illustrate this: in main function, given the nature of my class, it makes sense to have y[-8] (i.e. trying to access an array component of object y); however, within the overloaded operator function's definition for [], this array component reference is gibberish!!
I'll go and explore this dimension to the problem and let you guys know if it resolves it. In the meantime, thanks.
MiiNiPaa wrote: |
---|
You probably intended to normalize your range and access underlying array as |
Oh yeah! Although I had already come up with arrayPtr[index + (-1*arrayLowerBound)], essentially the same as yours. It's funny how even though I read all your prior posts, the epiphany I had on the indexing pitfall of my code did not occur until my last post. I observed you caught this pretty early on!
After making the necessary adjustments, I observed a marginal improvement in program behavior; however, I now see some strange, random, large numbers in the assignment section that I did not input in the first place! This aberrant behavior even for integer datatypes that I thought were in the clear before. Of course, the object input/output section for all datatypes still works fine; the assignment section of the code is where things go awry.
I have included a sample program output here for your observation:
Any insights?
If that wasn't changed, you got same problem here. Either use your array subscript operator to handle this, or corrext indexes manually.
A move assignment operator of class is a non-template non-static member function with the name operator= that takes exactly one parameter of type T&&, const T&&, volatile T&&, or constvolatile T&&.
[edit]Syntax
class_nameclass_name ( class_name ) | (1) | (since C++11) |
class_nameclass_name ( class_name ) = default; | (2) | (since C++11) |
class_nameclass_name ( class_name ) = delete; | (3) | (since C++11) |
[edit]Explanation
- Typical declaration of a move assignment operator.
- Forcing a move assignment operator to be generated by the compiler.
- Avoiding implicit move assignment.
The move assignment operator is called whenever it is selected by overload resolution, e.g. when an object appears on the left-hand side of an assignment expression, where the right-hand side is an rvalue of the same or implicitly convertible type.
Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, move-assigning from a std::string or from a std::vector may result in the argument being left empty. This is not, however, a guarantee. A move assignment is less, not more restrictively defined than ordinary assignment; where ordinary assignment must leave two copies of data at completion, move assignment is required to leave only one.
[edit]Implicitly-declared move assignment operator
If no user-defined move assignment operators are provided for a class type (struct, class, or union), and all of the following is true:
- there are no user-declared copy constructors;
- there are no user-declared move constructors;
- there are no user-declared copy assignment operators;
- there are no user-declared destructors;
| (until C++14) |
then the compiler will declare a move assignment operator as an member of its class with the signature .
A class can have multiple move assignment operators, e.g. both T& T::operator=(const T&&) and T& T::operator=(T&&). If some user-defined move assignment operators are present, the user may still force the generation of the implicitly declared move assignment operator with the keyword .
The implicitly-declared (or defaulted on its first declaration) move assignment operator has an exception specification as described in dynamic exception specification(until C++17)exception specification(since C++17)
Because some assignment operator (move or copy) is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.
[edit]Deleted implicitly-declared move assignment operator
The implicitly-declared or defaulted move assignment operator for class is defined as deleted if any of the following is true:
- has a non-static data member that is const;
- has a non-static data member of a reference type;
- has a non-static data member that cannot be move-assigned (has deleted, inaccessible, or ambiguous move assignment operator);
- has direct or virtual base class that cannot be move-assigned (has deleted, inaccessible, or ambiguous move assignment operator);
| (until C++14) |
A deleted implicitly-declared move assignment operator is ignored by overload resolution. | (since C++14) |
[edit]Trivial move assignment operator
The move assignment operator for class is trivial if all of the following is true:
- It is not user-provided (meaning, it is implicitly-defined or defaulted);
- has no virtual member functions;
- has no virtual base classes;
- the move assignment operator selected for every direct base of is trivial;
- the move assignment operator selected for every non-static class type (or array of class type) member of is trivial;
| (since C++14) |
A trivial move assignment operator performs the same action as the trivial copy assignment operator, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially move-assignable.
[edit]Implicitly-defined move assignment operator
If the implicitly-declared move assignment operator is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used.
For union types, the implicitly-defined move assignment operator copies the object representation (as by std::memmove).
For non-union class types (class and struct), the move assignment operator performs full member-wise move assignment of the object's direct bases and immediate non-static members, in their declaration order, using built-in assignment for the scalars, memberwise move-assignment for arrays, and move assignment operator for class types (called non-virtually).
As with copy assignment, it is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined move assignment operator: struct V { V& operator=(V&& other){// this may be called once or twice// if called twice, 'other' is the just-moved-from V subobjectreturn*this;}};struct A :virtual V {};// operator= calls V::operator=struct B :virtual V {};// operator= calls V::operator=struct C : B, A {};// operator= calls B::operator=, then A::operator=// but they may only called V::operator= once int main(){ C c1, c2; c2 = std::move(c1);} | (since C++14) |
[edit]Notes
If both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either a prvalue such as a nameless temporary or an xvalue such as the result of std::move), and selects the copy assignment if the argument is an lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.
It is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined move assignment operator (same applies to copy assignment).
See assignment operator overloading for additional detail on the expected behavior of a user-defined move-assignment operator.
[edit]Example
Run this code
Output:
0 Thoughts to “C++ Template Assignment Operator Overloading”