After doing some experimentation with move semantics with an array type I created, I am wondering why Microsoft's C++ compiler calls the move constructor when returning from a method by value whilst the Clang compiler elides the copy all together?
Is this correct or incorrect behaviour from Clang? or correct behaviour from Microsoft?
#include <algorithm>
#include <iostream>
template<typename T>
class Array {
public:
template<typename E>
class ArrayIterator {
public:
ArrayIterator(Array<E>& elements, int index) : position_(index), elements_(elements) {
}
T& operator * () {
return elements_[position_];
}
ArrayIterator& operator++ () {
position_++;
return *this;
}
ArrayIterator operator++ (int) {
return ArrayIterator(elements_, ++position_);
}
bool operator != (ArrayIterator const & other) {
return position_ != other.position_;
}
private:
int position_;
Array<E>& elements_;
};
typedef ArrayIterator<T> iterator;
Array();
explicit Array(int size);
~Array();
Array(const Array& other);
Array(Array&& other);
Array<T>& operator = (Array other);
T& operator[](int index);
int size() const;
iterator begin();
iterator end();
private:
void internal_swap(Array& other);
T *elements_;
int length_;
};
template<typename T>
Array<T>::Array() {
length_ = 0;
elements_ = 0;
}
template<typename T>
Array<T>::Array(int size) {
elements_ = new T[size];
length_ = size;
}
template<typename T>
Array<T>::~Array() {
delete[] elements_;
std::cout << "Destroy...." << std::endl;
}
template<typename T>
Array<T>::Array(const Array<T>& other) {
std::cout << "copy ctor" << std::endl;
length_ = other.size();
T *elements = new T[size()];
std::copy(other.elements_, other.elements_ + other.size(), elements);
elements_ = elements;
}
template<typename T>
Array<T>::Array(Array<T>&& other) {
std::cout << "move ctor" << std::endl;
length_ = other.size();
T* oelements = other.elements_;
other.elements_ = 0;
this->elements_ = oelements;
}
template<typename T>
Array<T>& Array<T>::operator = (Array other) {
internal_swap(other);
return *this;
}
template<typename T>
T& Array<T>::operator[](int index) {
return elements_[index];
}
template<typename T>
int Array<T>::size() const {
return length_;
}
template<typename T>
typename Array<T>::iterator Array<T>::begin() {
return iterator(*this, 0);
}
template<typename T>
typename Array<T>::iterator Array<T>::end() {
return iterator(*this, size());
};
template<typename T>
void Array<T>::internal_swap(Array& other){
T* oelements = other.elements_;
other.elements_ = this->elements_;
this->elements_ = oelements;
}
Array<int> get_values(int x);
int main(int argc, const char *argv[]) {
Array<int> a = get_values(2);
for (Array<int>::iterator i = a.begin(); i != a.end(); ++i) {
std::cout << *i << std::endl;
}
return 0;
}
Array<int> get_values(int x) {
Array<int> a(10);
if(x == 1) return a;
for (int i = 0; i <= 9; i++) {
a[i] = 1 + i;
}
return a;
}
Copy elision is one of those rare optimizations where the standard allows different observable behavior (it doesn't fall under the as-if rule), yet isn't undefined behavior.
Whether any copy or move constructor is called or elided in this context is unspecified, and different compilers can behave differently and both be correct.
Related
I have come across many occasions where I want to have an item which is selected inside a vector, for this I have written the template class:
// a vector wrapper which allows a specific item to be currently selected
template<typename T>
class VectorSelectable
{
public:
VectorSelectable() {};
VectorSelectable(std::initializer_list<T> items) : m_Items(items) {};
void Add(const T& v) { m_Items.push_back(v); m_CurrentIndex = m_Items.size()-1; } // lvalue & refs
void Add(T&& v) { m_Items.push_back(std::move(v)); m_CurrentIndex = m_Items.size()-1; } // rvalue
void Remove(size_t index) {
assert(index < m_Items.size());
m_Items.erase(m_Items.begin() + index);
if(m_CurrentIndex != -1 && (int)index <= m_CurrentIndex)
m_CurrentIndex--;
}
void RemoveCurrent() { assert(m_CurrentIndex > -1 && m_CurrentIndex < (int)m_Items.size()); Remove(m_CurrentIndex); }
T& CurrentItem() { assert(m_CurrentIndex > -1 && m_CurrentIndex < (int)m_Items.size()); return m_Items[m_CurrentIndex]; }
T& operator [](size_t index) { assert(index < Size()); return m_Items[index]; }
// moves value of n_next onto n, and n_new onto n
void ItemSwap(size_t n, size_t n_Next) {
assert(n < m_Items.size());
assert(n_Next < m_Items.size());
T itemBuf = std::move(m_Items[n]);
m_Items[n] = m_Items[n_Next];
m_Items[n_Next] = std::move(itemBuf);
}
size_t Size() { return m_Items.size(); }
const std::vector<T>& Data() { return m_Items; }
std::vector<T>* DataPtr() { return &m_Items; }
T* ItemPtr(size_t index) { assert(index < m_Items.size()); return &m_Items[index]; }
void SetCurrentIndex(int index) { assert(index >= -1 && index < (int)m_Items.size()); m_CurrentIndex = index; }
int& CurrentIndex() { return m_CurrentIndex; }
bool HasItemSelected() { return m_CurrentIndex != -1; }
private:
std::vector<T> m_Items;
int m_CurrentIndex = -1;
};
I am also coming across many scenarios where I want a vector of unique_ptrs (generally for polymorphic classes), this looks like this:
template<typename T>
class Vector_UniquePtrs
{
public:
// Adds an Item (and returns a raw ptr to it)
// usage: v.Add() ... (equivelent to v.Add<base_class>())
template<typename... Args>
T* Add(Args... args) {
return Add<T>(args...);
}
// Adds a Polymorphic Item (and returns a raw ptr to it)
// usage: v.Add<sub_class>()
template<typename T2, typename... Args>
T* Add(Args... args) {
m_Items.push_back(std::unique_ptr<T>(new T2(args...)));
return m_Items.back().get();
}
// Remove Item
void Remove(size_t index) {
assert(index < m_Items.size());
m_Items.erase(m_Items.begin() + index);
}
T* operator [](size_t index) { assert(index < Size()); return m_Items[index].get(); }
size_t Size() { return m_Items.size(); }
private:
std::vector<std::unique_ptr<T>> m_Items;
};
My question is:
How can I handle a combination of these 2 class types (e.g. VectorSelectable<unique_ptr>) as one returns ptrs, the other returns references, is the only option to write an entirely new class?
You mainly need to put the std::vector<std::unique_ptr<T>> in VectorSelectable and hide all the pointer stuff from the interface. With a few small changes to your class, it could look like this:
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
template <typename T>
class VectorPtrSelectable {
public:
VectorPtrSelectable() = default;
VectorPtrSelectable(std::initializer_list<T> items) :
m_CurrentIndex(items.size() - 1)
{
m_Items.reserve(items.size());
// fill `m_Items` from the initializer list ...
std::transform(items.begin(), items.end(), std::back_inserter(m_Items),
[](const T& item) {
// ... by creating a unique_ptr from each element (transformation)
return std::make_unique<T>(item);
});
};
template <class U, class... Args>
T& Add(Args&&... args) {
// make `Add` forward to `make_unique`
m_Items.emplace_back(std::make_unique<U>(std::forward<Args>(args)...));
m_CurrentIndex = m_Items.size() - 1;
// and return a reference instead
return *m_Items.back();
}
template <class... Args>
T& Add(Args&&... args) {
// forward to Add<U>
return Add<T>(std::forward<Args>(args)...);
}
void Remove(size_t index) {
m_Items.erase(std::next(m_Items.begin(), index));
if (m_CurrentIndex != static_cast<size_t>(-1) && index <= m_CurrentIndex)
m_CurrentIndex--;
}
T& operator[](size_t index) { return *m_Items[index]; }
const T& operator[](size_t index) const { return *m_Items[index]; }
T& CurrentItem() { return *m_Items[m_CurrentIndex]; }
const T& CurrentItem() const { return *m_Items[m_CurrentIndex]; }
void SetCurrentIndex(size_t index) { m_CurrentIndex = index; }
void RemoveCurrent() { Remove(m_CurrentIndex); }
bool HasItemSelected() { return m_CurrentIndex != static_cast<size_t>(-1); }
void ItemSwap(size_t n, size_t n_Next) {
// simplified swapping:
std::swap(m_Items[n], m_Items[n_Next]);
}
// make functions that does not change your instance const qualified:
size_t CurrentIndex() const { return m_CurrentIndex; }
size_t Size() const { return m_Items.size(); }
private:
std::vector<std::unique_ptr<T>> m_Items;
size_t m_CurrentIndex = static_cast<size_t>(-1); // size_t for the index
};
Example usage:
#include <iostream>
#include <string>
int main() {
VectorPtrSelectable<std::string> vs{"World", "Hello"};
std::cout << vs.CurrentItem() << '\n';
vs.ItemSwap(0, 1);
std::cout << vs.CurrentItem() << '\n';
vs.RemoveCurrent();
std::cout << vs.CurrentItem() << '\n';
std::cout << vs.Add("Add and get a reference") << '\n';
}
Output:
Hello
World
Hello
Add and get a reference
I made m_CurrentIndex a size_t because that's idiomatic but if you'd like to keep it as an int, that's fine too.
std::next(m_Items.begin(), index) will do the same as m_Items.begin() + index, but in cases where the iterator returned by m_Items.begin() is a plain pointer, using std::next avoids potential warnings about using pointer arithmetic.
Returning a reference instead of a pointer to the added element makes no difference other than making the interface more idiomatic. It's simply what a user of the class is likely to expect. Returning a pointer also opens up questions like "can it return nullptr?" etc.
The added const qualified functions makes those functions usable in const contexts too.
template<class T>
void foo(const VectorPtrSelectable<T>& vps) { // note: const&
if(vps.Size() > 0) {
std::cout << "the first element is " << vps[0] << '\n';
std::cout << "the current element is " << vps.CurrentItem() << '\n';
}
}
None of the three member functions used above could be used without the const qualified overloads.
I'm trying this little exercice from my c++ class and i've manage to do this from now.
It's not fully completed, but for now i'm facing the eternal problem of template of internal class.
I've seen a lot of different solution here on stack and on other websites, but still missing someting.
+ my goal at the end is also to understand the "why".
It's an Iterator internal class needed to iterate to my array. I've seen some exemple giving 2 differents typename for External and Iternal class and then using the typedef, but i'm not sure what's the best way to implement it.
As you can understand my Iterator class need to take the same kind of type as my Array class.
I for sure will need to change some function signatures and add some <\T> here and there, but for now i just need to avoid all the template traps.
using namespace std;
template <typename T>
class Iterator;
template<typename T>
class Array{
T* _data;
size_t _size;
Iterator* _start;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T* operator*(){
return content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o):_size(o._size), _data(o._data), _start(o._start){}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data[0]);
}
Iterator end(){
return Iterator(_data[_size-1]);
}
};
Can you please give me a clue or help me with this.
For more here is my basic main:
#include "Array.h"
int main() {
Array<string> array({"h","e","l","l","o"});
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << *i << endl;
return 0;
}
Thank you!
There is no template Iterator at the global scope, so this is wrong:
template <typename T>
class Iterator;
Also, Array<T>::Iterator isn't a template, it's just an inner class. You can simply forward-declare it inside the class like this:
template<typename T>
class Array {
public:
class Iterator;
Then there are some bugs in your code (e.g. end() should be 1 past the last element, you need to dereference the iterator twice and construct one from a pointer).
Here's a fixed version:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Array {
T* _data;
size_t _size;
public:
class Iterator;
private:
Iterator* _start;
public:
class Iterator {
T* content;
public:
explicit Iterator(T* value) :content(value) {}
Iterator& operator++() {
++content;
return *this;
}
T* operator*() {
return content;
}
bool operator ==(const Iterator& o)const {
if (content == o.content) {
return true;
}
return false;
}
bool operator !=(const Iterator& o)const {
return !(*this == o);
}
};
Array(const size_t s) :_size(s) {
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o) :_size(o._size), _data(o._data), _start(o._start) {}
Array(const std::initializer_list<T>& list) :_size(list.size()) {
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while (start != fin) {
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array() {
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o) {
if (this != &o) {
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index) {
if (index < _size) {
return *_data[index];
}
}
const size_t size()const {
return _size;
}
Iterator begin() {
return _start;
}
Iterator end() {
return Iterator(_data + _size);
}
};
int main() {
Array<string> array({ "h","e","l","l","o" });
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << **i << endl;
}
Thank you #rustyx for your help,
Wasn't far from it, but realy thank you.
I will post my corrected and working code here in case it can help others.
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
template<typename T>
class Array{
T* _data;
size_t _size;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T& operator*(){
return *content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
}
Array(const Array& o):_size(o._size){
_data = new T[_size];
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data);
}
Iterator end(){
return Iterator(_data + _size);
}
};
I know how to overload operator[] as follows :
T& operator [](int idx) {
return TheArray[idx];
}
T operator [](int idx) const {
return TheArray[idx];
}
But what I want is to control values assigned by arr[i] = value.
I want to control value to be between 0 and 9.
Is there any syntax to do so?
You would have to write a template class that holds a reference to the element in the array (of type T), in this template you implement the assignment operator, and there you can implement your check. Then you return an object of this template class from your [] operator.
Something like this:
template< typename T> class RangeCheck
{
public:
RangeCheck( T& dest): mDestVar( dest) { }
RangeCheck& operator =( const T& new_value) {
if ((0 <= new_value) && (new_value < 9)) { // <= ??
mDestVar = new_value;
} else {
... // error handling
}
return *this;
}
private:
T& mDestVar;
};
Rene has provided a good answer. In addition to this, here is a full example. Note that I added a "user-defined conversion", i.e., operator T, in the proxy_T class.
#include <iostream>
#include <array>
#include <stdexcept>
template <class T>
class myClass
{
std::array<T, 5> TheArray; // Some array...
class proxy_T
{
T& value; // Reference to the element to be modified
public:
proxy_T(T& v) : value(v) {}
proxy_T& operator=(T const& i)
{
if (i >= 0 and i <= 9)
{
value = i;
}
else
{
throw std::range_error(std::to_string(i));
}
return *this;
}
operator T() // This is required for getting a T value from a proxy_T, which make the cout-lines work
{
return value;
}
};
public:
proxy_T operator [](int const idx)
{
return TheArray.at(idx);
}
T operator [](int const idx) const
{
return TheArray[idx];
}
};
int main() {
myClass<int> A;
std::cout << A[0] << std::endl;
A[0] = 2;
std::cout << A[0] << std::endl;
A[1] = 20;
}
My abstract Reference counter class:
template<class T>
class ReferenceCounter
{
public:
ReferenceCounter();
~ReferenceCounter();
void addRef();
void release();
uint32 getCountReferences() const;
protected:
int32* pCountReferences;
virtual void destroyObject() = 0;
virtual void shallowCopy(const T& rhs) = 0;
};
template<class T>
inline ReferenceCounter<T>::ReferenceCounter()
{
pCountReferences = new int32;
*pCountReferences = 1;
}
template<class T>
inline ReferenceCounter<T>::~ReferenceCounter()
{
if(pCountReferences != NULL && *pCountReferences == 0)
{
delete pCountReferences;
pCountReferences = NULL;
}
}
template<class T>
inline void ReferenceCounter<T>::addRef()
{
debug_assert((*pCountReferences) >= 0, "Incorrect value of count references");
++(*pCountReferences);
}
template<class T>
inline void ReferenceCounter<T>::release()
{
debug_assert((*pCountReferences) > 0, "Incorrect value of count references");
(*pCountReferences)--;
if(pCountReferences != NULL && *pCountReferences == 0)
{
destroyObject();
}
}
template<class T>
inline uint32 ReferenceCounter<T>::getCountReferences() const
{
return *pCountReferences;
}
This is my smart pointer :
template<class T>
class SmartPtr
{
public:
SmartPtr();
SmartPtr(T* pInst);
SmartPtr(const SmartPtr<T>& rhs);
~SmartPtr();
void operator = (const SmartPtr<T>& rhs);
T* operator -> () const;
T* getData() const;
bool isNULL() const;
private:
T* pInst;
};
template<class T>
SmartPtr<T>::SmartPtr() : pInst(NULL) {}
template<class T>
SmartPtr<T>::SmartPtr(T* pInst) : pInst(pInst) {}
template<class T>
SmartPtr<T>::~SmartPtr()
{
if(pInst != NULL)
{
pInst->release();
}
}
template<class T>
SmartPtr<T>::SmartPtr(const SmartPtr<T>& rhs)
{
this->pInst = rhs.pInst;
if(pInst != NULL)
{
pInst->addRef();
}
}
template<class T>
void SmartPtr<T>::operator= (const SmartPtr<T>& rhs)
{
this->pInst = rhs.pInst;
if(pInst != NULL)
{
pInst->addRef();
}
}
template<class T>
T* SmartPtr<T>::operator->() const
{
return pInst;
}
template<class T>
T* SmartPtr<T>::getData() const
{
return pInst;
}
template<class T>
bool SmartPtr<T>::isNULL() const
{
return pInst == NULL;
}
There are test of code :
#include <iostream>
#include "ReferenceCounter.h"
#include "SmartPtr.h"
using namespace std;
class B;
class A : public ReferenceCounter<A>
{
public:
A();
A(const A& rhs);
~A();
SmartPtr<B> getB();
void operator = (const A& rhs);
private:
void destroyObject();
void shallowCopy(const A& rhs);
};
class B : public ReferenceCounter<B>
{
private:
void destroyObject() {} ;
void shallowCopy(const B& rhs) {};
};
A::A()
{
cout << "Create object" << endl;
}
A::A(const A& rhs)
{
shallowCopy(rhs);
addRef();
cout << "copy constructor " << endl;
}
A::~A()
{
release();
}
void A::destroyObject()
{
cout << "destroy" << endl;
}
void A::shallowCopy(const A& rhs)
{
this->pCountReferences = rhs.pCountReferences;
}
void A::operator = (const A& rhs)
{
shallowCopy(rhs);
addRef();
cout << "operator = " << endl;
}
SmartPtr<B> A::getB()
{
return SmartPtr<B>(new B());
}
SmartPtr<A> getA()
{
SmartPtr<A> a(new A());
return a;
}
int main()
{
getA();
return 0;
}
This code is worked but below not called copy constructor of smart pointer when i debug this code . What problems happens below ??
int main()
{
A a;
a.getB();
}
See here for Return value optimization.
The compiler is allowed, to eliminate the copy of a temporary object being returned.
With C++11, there's also the possibility of moving an object. See What are move semantics? for an explanation.
Update:
This is not a problem at all, just a compiler optimization.
As long as there's nothing special going on in your constructor and destructor, there's no need to prevent this optimization. You should allow this instead, because it makes your program run faster by skipping one constructor and one destructor call.
Not 100% sure whether my question is worded correctly as I don't fully understand my problem.
For my course I need to create my own smart pointer to clean up after itself.
Here's my code so far:
Header:
class Test
{
public:
Test()
{
m_iTest1 = 4;
m_iTest2 = 3;
m_iTest3 = 2;
m_iTest4 = 1;
}
Test (int a, int b, int c, int d)
{
m_iTest1 = a;
m_iTest2 = b;
m_iTest3 = c;
m_iTest4 = d;
}
Test(const Test& a_oTest)
{
m_iTest1 = a_oTest.m_iTest1;
m_iTest2 = a_oTest.m_iTest2;
m_iTest3 = a_oTest.m_iTest3;
m_iTest4 = a_oTest.m_iTest4;
}
~Test(){;}
int m_iTest1;
int m_iTest2;
int m_iTest3;
int m_iTest4;
};
template<class T>
class SmartData
{
public:
template<class T> friend class SmartPointer;
SmartData();
SmartData(const T& a_oData);
~SmartData();
T operator * () const;
unsigned int GetCount(){return m_uiCount;}
protected:
void IncrementCount(){++m_uiCount;}
void DecrementCount();
void DeleteThis();
unsigned int m_uiCount;
T* m_poData;
};
template<class T>
class SmartPointer
{
public:
SmartPointer();
SmartPointer(SmartData<T>& a_oSmartData);
SmartPointer(const SmartPointer& a_oSmartPointer);
~SmartPointer();
SmartPointer<T>& operator = (const SmartPointer<T>& a_oSmartPointer);
T operator *() const;
SmartData<T>* operator ->() const;
unsigned int GetCount() const;
private:
SmartData<T>* m_poSmartData;
};
#include "smartpointer.inl"
Inline file:
template<class T>
SmartData<T>::SmartData()
{
m_uiCount = 0;
m_poData = new T();
}
template<class T>
SmartData<T>::SmartData(const T& a_oData)
{
m_uiCount = 0;
m_poData = new T(a_oData);
}
template<class T>
SmartData<T>::~SmartData()
{
if (m_poData)
{
delete m_poData;
}
}
template<class T>
T SmartData<T>::operator * () const
{
return *m_poData;
}
template<class T>
void SmartData<T>::DecrementCount()
{
if (m_uiCount - 1 == 0 || m_uiCount == 0)
{
DeleteThis();
return;
}
--m_uiCount;
}
template<class T>
void SmartData<T>::DeleteThis()
{
if (m_poData)
{
delete m_poData;
m_poData = 0;
}
}
template<class T>
SmartPointer<T>::SmartPointer()
{
m_poSmartData = new SmartData<T>();
m_poSmartData->IncrementCount();
}
template<class T>
SmartPointer<T>::SmartPointer(SmartData<T>& a_oSmartData)
{
m_poSmartData = &a_oSmartData;
m_poSmartData->IncrementCount();
}
template<class T>
SmartPointer<T>::SmartPointer(const SmartPointer& a_oSmartPointer)
{
m_poSmartData = a_oSmartPointer.a_oSmartData;
m_poSmartData->IncrementCount();
}
template<class T>
SmartPointer<T>::~SmartPointer()
{
m_poSmartData->DecrementCount();
m_poSmartData = 0;
}
template<class T>
SmartPointer<T>& SmartPointer<T>::operator = (const SmartPointer<T>& a_oSmartPointer)
{
m_poSmartData = a_oSmartPointer.m_poSmartData;
m_poSmartData->IncrementCount();
}
template<class T>
T SmartPointer<T>::operator *() const
{
return *m_poSmartData->m_poData;
}
template<class T>
SmartData<T>* SmartPointer<T>::operator ->() const
{
return m_poSmartData;
}
template<class T>
unsigned int SmartPointer<T>::GetCount() const
{
return m_poSmartData->m_uiCount;
}
main.cpp
void SomeFunction1(SmartData<Test>& a_SmartData)
{
SmartPointer<Test> oSmartPointer2(a_SmartData);
}
void main()
{
SmartData<int> oSmartData1(5);
if (1)
{
SmartPointer<int> oSmartPointer1(oSmartData1);
int iTemp1 = oSmartPointer1->GetCount();
int iTemp2 = *oSmartPointer1;
int iTemp3 = *oSmartData1;
}
if (1)
{
SmartData<int> oSmartData2(5);
}
SmartData<Test> oSmartData3;
(*oSmartData3).m_iTest1 = 5; //Does not work
if (1)
{
SmartData<Test> oSmartData4(oSmartData3);
SomeFunction1(oSmartData3);
//oSmartData4 still exits
}
}
Everything works fine, the data is cleaned up after itself and I get no leaks... except for one line:
(*oSmartData3).m_iTest1 = 5;
I'm compiling with visual studio, and when I place the "." after "(*oSmartData3)"... "m_iTest1" comes up correctly. Except I get an error:
error C2106: '=' : left operand must be l-value
I'm not sure why this doesn't work or what to change so it does work.
Look closer at the declaration of operator*() in SmartData:
T operator * () const;
This means that this operator is returning an object of type T, which is a copy of m_poSmartData->m_poData. It is a temporary object in this context:
(*oSmartData3).m_iTest1 = 5; //Does not work
Of course, you cannot assign a value to a temporary object, because it is not an l-value. Read more about what l-values and r-values are here: http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.vacpp7a.doc%2Flanguage%2Fref%2Fclrc05lvalue.htm
I would suggest that you return a reference to m_poSmartData->m_poData
in operator*() (if I'm understanding correctly what you are trying to do).
Your T operator *() const is returning a temporary object (i.e. a copy), which is not an l-value (cannot be assigned to). Return a reference instead:
T& operator *() const;
Does this work:
oSmartData3.m_iTest1 = 5;