I am trying to create a class whose objects must contain a short description ("name") of what their value represent. Therefore the only public constructor should take a string as argument.
For the operations, however, I need to create temporary (no relevant name) object to calculate the value to be assigned to an already existing object. For that I have implemented a private constructor, which should not be used, neither directly nor indirectly, to instantiate a new object - these temporary objects should only be assigned to an already existing object, through operator=, which only copies the value rather than name and value.
The problem comes with the use of "auto". If a new variable is declared as follows:
auto newObj = obj + obj;
the compiler deduces the return type of operator+ and directly assign its result to newObj
. This results in an object with a irrelevant name, which should not be possible to instantiate.
Also, deducing the type of an already existing object should still be possible from some functions, like:
auto newObj = obj.makeNewObjWithSameTypeButOtherName("Other name");
Follows a code demonstrating the problem:
#include <iostream>
#include <string>
using namespace std;
template<class T>
class Sample
{
public:
Sample(const string&);
Sample<T> makeNewObj(const string&);
// Invalid constructors
Sample();
Sample(const Sample&);
void operator=(const Sample&);
void operator=(const T&);
Sample<T> operator+(const Sample&) const;
void show(void);
private:
// Private constructor used during operations
Sample(const T&);
T _value;
string _name;
};
template<class T>
Sample<T>::Sample(const string& name)
{
this->_name = name;
this->_value = 0;
}
template<class T>
Sample<T>::Sample(const T&value)
{
this->_name = "Temporary variable";
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::makeNewObj(const string& name)
{
return Sample<T>(name);
}
template<class T>
void
Sample<T>::operator=(const Sample& si)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = si._value;
}
template<class T>
void
Sample<T>::operator=(const T& value)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return Sample<T>( this->_value + si._value );
}
template<class T>
void
Sample<T>::show(void)
{
cout << _name << " = " << _value << endl;
}
int main()
{
Sample<double> a("a"), b("b");
a = 1; // Sample::operator=(const T&)
b = 2.2; // Sample::operator=(const T&)
a.show(); // Output: a = 1
b.show(); // Output: b = 2.2
auto c = a.makeNewObj("c"); // Should be possible
c = a + b; // Sample::operator+(const Sample&) and Sample::operator=(const Sample&)
c.show(); // Output: c = 3.2
// Sample<double> d; // Compiler error as expected: undefined reference to `Sample::Sample()'
// auto f = a; // Compiler error as expected: undefined reference to `Sample::Sample(Sample const&)'
// This is what I want to avoid - should result in compiler error
auto g = a+c; // No compiler error: uses the private constructor Sample::Sample(const T&)
g.show(); // Output: Temporary variable = 4.2 <-- !! Object with irrelevant name
}
Yes, we can access the private constructor or instantiate a class with private constructor. The java reflection API and the singleton design pattern has heavily utilized concept to access to private constructor.
A private constructor in Java ensures that only one object is created at a time. It restricts the class instances within the declared class so that no class instance can be created outside the declared class.
Yes, we can declare a constructor as private. If we declare a constructor as private we are not able to create an object of a class. We can use this private constructor in the Singleton Design Pattern.
We can declare a constructor private by using the private access specifier. Note that if a constructor is declared private, we are not able to create an object of the class. Instead, we can use this private constructor in Singleton Design Pattern.
A quick workaround is to not return a temporary Sample<T>
from operator +
. Since you only want the value part you can just return that instead. That changes the code to
T operator+(const Sample&) const;
template<class T>
T
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return this->_value + si._value;
}
and then
auto g = a+c;
will make g
whatever T
is and g.show();
will not compile as g
isn't a Sample<T>
.
Sample<double> g = a+c;
Will also not work as it tries to construct g
from a value and that constructor is private.
This will require adding
friend T operator+(T val, Sample<T> rhs) { return val + rhs._value; }
If you want to be able to chain additions like
a + a + a;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With