When creating template classes in C++, if the type upon which the template will
ID: 659094 • Letter: W
Question
When creating template classes in C++, if the type upon which the template will be specialized is intended to be a shared_ptr type, is it better to make T a shared_ptr type or make T a non-pointer type?
For example, which of the below classes is better, FirstExample or SecondExample? Or alternatively, is this entirely context-dependent? If so, can you an example of a context where FirstExample is better and a context where SecondExample is better?
In my case, clarity of usage is a higher priority than performance.
#include <iostream>
#include <memory>
using IntPtr = std::shared_ptr<int>;
template <class T>
class FirstExample
{
public:
FirstExample( std::shared_ptr<T> value )
:myData( value ) {}
std::shared_ptr<T> getData() const { return myData; }
private:
std::shared_ptr<T> myData;
};
template <class T>
class SecondExample
{
public:
SecondExample( const T& value )
:myData( value ) {}
T getData() const { return myData; }
private:
T myData;
};
int main(int argc, const char * argv[])
{
FirstExample<int> firstInstance( std::make_shared<int>( 10 ) );
IntPtr copyOfFirstInstanceData = firstInstance.getData();
SecondExample<IntPtr> secondInstance( std::make_shared<int>(10) );
IntPtr copyOfSecondInstanceData = secondInstance.getData();
return 0;
}
Explanation / Answer
Let's first adjust the user-defined ctors for equivalence:
Use pass-by-value and move-construction:
FirstExample( std::shared_ptr<T> value ) : myData(std::move(value)) {}
SecondExample( T value ) : myData(std::move(value)) {}
Use pass-by-reference and copy-construction:
FirstExample( const std::shared_ptr<T>& value ) : myData(value) {}
SecondExample( const T& value ) : myData(value) {}
Use pass-by-reference and move-/copy-construction:
FirstExample( const std::shared_ptr<T>& value ) : myData(value) {}
SecondExample( const T& value ) : myData(value) {}
FirstExample( std::shared_ptr<T>&& value ) : myData(std::move(value)) {}
SecondExample( T&& value ) : myData(std::move(value)) {}
As an aside, I would change getData too, in order to avoid copyng where not needed:
const T& getData() const { return myData; }
const std::shared_ptr<T>& getData() const { return myData; }
Now, ThirdExample behaves the same as FirstExample:
template<class T> using ThirdExample = SecondExample<std::shared_ptr<T>>;
The advantage to using the first one is less typing and possibly better error-messages.
The advantage to using the second is more flexibility, as you can use another smart-pointer, as long as it offers the proper interface.
But why should you have to decide between either?
As ThirdExample demonstrates, you can implement the second example, and provide the first as a simple alias-template for convenience, just like the standard-library does for std::integer_sequence and std::index_sequence.
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.