With the code:
template <typename T>
class MyClass
{
public:
MyClass(const MyClass& other)
{
//Explicit, types must both match;
}
template <typename U>
MyClass(const MyClass<U>& other)
{
//Implicit, types can copy across if assignable.
//<?> Is there a way to make this automatically happen for memberwise
//copying without having to define the contents of this function?
this->value = other.getValue();
}
privte:
T value;
};
The following cases would be true:
MyClass<float> a;
MyClass<float> b = a; //Calls explicit constructor which would have been auto-generated.
MyClass<int> c;
MyClass<float> d = c; //Calls implicit constructor which would not have been auto gen.
My question is: is there a way to get an implicit copy constructor like this, but not have to define how it works? Copy, assignment, and standard constructors are all provided when a class is created by default... it would be nice to be able to get an implicit conversion constructor as well for free if possible when using templates.
I'm guessing its not possible but can someone explain why that is to me if that is the case?
Thanks!
You can't make the compiler generate a pseudo-copy-constructor-template for you. Why? Because MyClass<T> can be something entirely different from MyClass<U>
This isn't my original answer. I had misunderstood the question, sorry
Related
We know that a built-in array can neither be copied nor be assigned. So If it is a member data of a class/struct/union It is OK to leave the compiler do its magic to copy them:
struct ArrInt5{
ArrInt5() = default;
ArrInt5(ArrInt5 const&) = default; // OK
int a[5];
};
ArrInt5 a, b = a; // OK
Sometimes that is not the case for example if the array holds objects of non-default- constructible objects. in that
case we do need to define our copy-ctor to do the job:
struct Bar{
// deleted default ctor
Bar(int x) : x_(x){}
int x_ = 0;
};
struct Foo{
Foo();
Foo(Foo const&);
Bar arr_[5];
};
Foo::Foo() : arr_{0, 1, 2, 3, 4}
{}
Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}
{}
As you can see Foo has a built-in array of five objects of type struct Bar that type is not default-constructible so the default ctor and the copy ctor must initialize it (arr_).
The problem is: How could initialize that array if it is of a big size lets say 1 million element? Should I hard copy them element by element? or there's some workaround?
I know so many will recommend me to use the equivalent STL container std::array but I'm not on that topic, I'm asking whether there's a workaround for my built-in array of non default-constructible objects.
The only way to generate this effect for you automatically is to wrap your array in a class type of some kind that has a compiler-generated copy constructor (e.g. such as a defaulted constructor). In your particular example, you could just have Foo(const Foo&) be defaulted:
struct Foo{
Foo();
Foo(Foo const&) = default;
// This generates your:
// "Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}"
// by the compiler
Bar arr_[5];
};
However the above only works if your copy-logic is trivial. For more complex copying, you can't always have a default copy constructor -- but we can solve this more generally.
If you wrap an array data member of any type/size in a struct or class and make sure it has compiler-generated copy constructors, then copying is easy in a constructor.
For example:
template <typename T, std::size_t N>
struct Array {
...
T data[N];
};
This allows for the simple case of copying:
Array<Bar,1000000> a = {{...}};
Array<Bar,1000000> b = a;
Or the more complex case of copying in a constructor that might require more logic than the compiler-generated ones:
class Foo {
...
Foo(const Foo& other);
...
Array<Bar,100000> arr_;
};
Foo::Foo(const Foo& other)
: arr_{other.arr_}
{
... other complex copy logic ...
}
At which point, congratulations -- you have invented std::array!
std::array was added to the standard library exactly because it was this simple to introduce a library-solution to this problem without having to alter the language itself. Don't shy away from it.
I have some classes that look like this:
struct foo
{
foo() = delete;
explicit foo(int id) :
id(id)
{}
virtual ~foo() = default;
int id;
};
template<typename T>
struct MyClass : foo
{
MyClass() = delete;
MyClass(int id, T value) :
foo(id),
value(value)
{}
T value;
}
And it has worked pretty well up until now where T is a double, a bool, etc.
Unfortunately, CPU manufacturers decided to start putting more logical cores in their products, and now I have to write a bunch of multithreaded code (thanks guys).
The problem arises when trying to do something like
MyClass<std::atomic_flag> bar{0, ?????????????} //ATOMIC_FLAG_INIT doesnt fix it
As it attempts to use the deleted copy constructor std::atomic_flag(const std::atomic_flag&). I can see where its trying to do the copy, and the only possible solution I've come up with would involve manual allocation of memory and almost by-hand construction of members using placement-new.
Is there an easier way of doing this? How might the constructor of MyClass<T> be structured to allow generic types as well as special things that don't want to be moved/copied?
I'm using MSVC v140, so no C++17 or later features :(
Rather than taking an instance of the object to create, you can construct it inside your object by instead taking parameters of the type you wish to construct your class with. This will not work directly with std::atomic_flag due to the limitations of initializing with ATOMIC_FLAG_INIT (until C++20, that is), but would be fine with something like std::atomic<bool>.
For example:
template <typename T>
struct MyClass : foo {
MyClass() = delete;
// list of parameters to pass to value's constructor
// if this is only going to be with primitive-like types,
// you could take Args by value instead of forwarding reference
template <typename... Args>
explicit MyClass(int id, Args&&... args)
: foo(id), value(std::forward<Args>(args)...)
{}
T value;
};
Then you should be able to do
MyClass<std::atomic<bool>> bar{0, false};
If I have a class like this
template <typename T>
class MyClass
{
T myData;
public:
T getValue() { return myData; }
template <typename V>
MyClass(const MyClass<V>& other) :
myData((T) other.getValue())
{
}
};
This would mean that I provide a copy constructor (for V=T) and thus according to this link Why no default move-assignment/move-constructor? I do not get default move constructors etc.
Is there a way to have the templated constructor only work as conversion constructors, so for V!=T?
Your premise is wrong. A constructor template is never used to instantiate a copy (or move) constructor. In other words, a copy/move constructor is always a non-template member function, even if a member function template could produce a constructor with the appropriate copy/move signature.
So as is, your class will still have a normal copy constructor in addition to the template.
I have a class that I'm writing that will take a special type for one of its constructors that could be any type that fits my requirements. I have run into the issue that this templated constructor causes my copy and move constructors to be illegal overloads!
My class is layed out like this:
template<typename ... Types>
class myclass{
public:
myclass(const myclass &other){/* copy constructor */}
myclass(myclass &&other){/* move constructor */}
template<typename Special>
myclass(Special &&arg){/* stops copy/move implementations */}
}
How can I get around this limitation?
Constrain it.
template<typename Special,
std::enable_if_t<!std::is_same<std::decay_t<Special>, myclass>{}, int> = 0 >
myclass(Special &&arg) { /* ... */ }
Depending on your particular use case, you may also want to constrain Special further to only types that fit your requirements.
This example shows the different cases:
const myclass c1(42); // special: int
myclass c2(c1); // copy
myclass c3(c2); // special: myclass& (non const)
myclass c4(std::move(c3)); // move
Live Demo
your copy/move constructors are still legal overloads, but non const l-value has an exact match with your template constructor.
You may:
use SFINAE to forbid myclass& in the template (as in T.C's answer)
provide an other overload (with exact match):
myclass(myclass &other) : myclass(static_cast<const myclass &>(other)) {}
Demo
I feel like this one has been asked before, but I'm unable to find it on SO, nor can I find anything useful on Google. Maybe "covariant" isn't the word I'm looking for, but this concept is very similar to covariant return types on functions, so I think it's probably correct. Here's what I want to do and it gives me a compiler error:
class Base;
class Derived : public Base;
SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error
Assume those classes are fully fleshed out... I think you get the idea. It can't convert a SmartPtr<Derived> into a SmartPtr<Base> for some unclear reason. I recall that this is normal in C++ and many other languages, though at the moment I can't remember why.
My root question is: what is the best way to perform this assignment operation? Currently, I'm pulling the pointer out of the SmartPtr, explicitly upcasting it to the base type, then wrapping it in a new SmartPtr of the appropriate type (note that this is not leaking resources because our home-grown SmartPtr class uses intrusive reference counting). That's long and messy, especially when I then need to wrap the SmartPtr in yet another object... any shortcuts?
SmartPtr<Base> and SmartPtr<Derived> are two distinct instantiations of a the SmartPtr template. These new classes do not share the inheritance that Base and Derived do. Hence, your problem.
what is the best way to perform this assignment operation?
SmartPtr<Base> b = d;
Does not invoke assignment operator. This invokes the copy-ctor (the copy is elided in most cases) and is exactly as if you wrote:
SmartPtr<Base> b(d);
Provide for a copy-ctor that takes a SmartPtr<OtherType> and implement it. Same goes for the assignment operator. You will have to write out the copy-ctor and op= keeping in mind the semantics of SmartPtr.
Both the copy constructor and the assignment operator should be able to take a SmartPtr of a different type and attempt to copy the pointer from one to the other. If the types aren't compatible, the compiler will complain, and if they are compatible, you've solved your problem. Something like this:
template<class Type> class SmartPtr
{
....
template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor
template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};
Templates are not covariant, and that's good; imagine what would happen in the following case:
vector<Apple*> va;
va.push_back(new Apple);
// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!
To achieve what you are trying to do, the SmartPointer class must have a templatized constructor, that takes either another SmartPointer or a pointer of another type. You could have a look at boost::shared_ptr, which does exactly that.
template <typename T>
class SmartPointer {
T * ptr;
public:
SmartPointer(T * p) : ptr(p) {}
SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}
template <typename U>
SmartPointer(U * p) : ptr(p) {}
template <typename U>
SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}
// Do the same for operator= (even though it's not used in your example)
};
Depends on the SmartPtr class. If it has a copy constructor (or in your case, assignment operator) that takes SmartPtr<T>, where T is the type it was constructed with, then it isn't going to work, because SmartPtr<T1> is unrelated to SmartPtr<T2> even if T1 and T2 are related by inheritance.
However, if SmartPtr has a templatized copy constructor/assignment operator, with template parameter TOther, that accepts SmartPtr<TOther>, then it should work.
Assuming you have control of the SmartPtr class, the solution is to provide a templated constructor:
template <class T>
class SmartPtr
{
T *ptr;
public:
// Note that this IS NOT a copy constructor, just another constructor that takes
// a similar looking class.
template <class O>
SmartPtr(const SmartPtr<O> &src)
{
ptr = src.GetPtr();
}
// And likewise with assignment operator.
};
If the T and O types are compatible, it will work, if they aren't you'll get a compile error.
I think the easiest thing is to provide automatic conversion to another SmartPtr according to the following:
template <class T>
class SmartPtr
{
public:
SmartPtr(T *ptr) { t = ptr; }
operator T * () const { return t; }
template <class Q> operator SmartPtr<Q> () const
{ return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
T *t;
};
Note that this implementation is robust in the sense that the conversion operator template does not need to know about the semantics of the smart pointer, so reference counting does not need to replicated etc.