constructor call is ambiguous...

A

aaragon

Hello! I've been working on enhancing a simple Matrix class that I
found on Stroustrup's "The C++ Programming Language" and I have a
problem with ambiguous constructor calls that I don't know if I can
solve.

The idea is:

template <typename T = double>
class Matrix
{
typedef T ValueType;
valarray<ValueType>* v_; //!< Valarray object used to store the
matrix elements by rows
size_t m_; //!< Unsigned integer used to
store the number of rows
size_t n_; //!< Unsigned integer used to
store the number of columns

public:
// matrix constructors
Matrix() : m_(), n_(), v_(NULL) {}
explicit Matrix(size_t n, ValueType val = 0) : m_(n), n_(n),
v_(new valarray<ValueType>(val, n*n)) {}
Matrix(size_t m, size_t n, ValueType val = 0) : m_(m), n_(n),
v_(new valarray<ValueType>(val,m*n)){}
Matrix(const Matrix& prototype) {
// copy number of rows and columns from matrix A
m_ = prototype.m_;
n_ = prototype.n_;
// allocates memory for storing the matrix elements
v_ = new valarray<ValueType>(*(prototype.v_));
}
Matrix& operator=(const Matrix& prototype) {
// check to avoid self-copy
if(this != &prototype)
{
// deletes dynamically allocated memory
delete v_;
// copy number of rows and columns from matrix A
m_ = prototype.m_;
n_ = prototype.n_;
// allocates memory for storing the matrix elements
v_ = new valarray<ValueType>(*(prototype.v_));
}
return *this;
}
....
....
....

Now, as you can see the second and third constructor calls are
ambiguous. Is there a way to get around this? I would like to have
both constructors. Also, I've been thinking in the best way to have a
constructor for identity matrices. I thought that makeing a
constructor:

Matrix(size_t, string);

would make a good choice and then comparing the string agains
"identity" or something like this. But maybe there is a better way to
do it, probably with an enumerated type?

Thanks for your suggestions!

 
R

Ron Natalie

You could always take a pair<int,int> to force the MxN constructor.

I also suggest style wise that you default the args
ValueType val = ValueType()
rather than zero.

That way you can use it with anything that has default
initialization semantics (and yes it still works with the
stupided-assed inconsistant pod types. Default initialization
for them is still zero initialization even if the compiler
haphazardsly omits doing it).
 
A

aaragon

You could always take a pair<int,int> to force the MxN constructor.

I also suggest style wise that you default the args
ValueType val = ValueType()
rather than zero.

That way you can use it with anything that has default
initialization semantics (and yes it still works with the
stupided-assed inconsistant pod types. Default initialization
for them is still zero initialization even if the compiler
haphazardsly omits doing it).

Thanks for the tip on initialization. Regarding the pair<>, I don't
think that's a good idea because clients probably won't have idea
about that class. I would like them to write

Matrix<double> A(4,3);

and not

Matrix<double> A(std::make_pair(4,3));
 
J

James Kanze

Hello! I've been working on enhancing a simple Matrix class that I
found on Stroustrup's "The C++ Programming Language" and I have a
problem with ambiguous constructor calls that I don't know if I can
solve.
The idea is:
template <typename T = double>
class Matrix
{
typedef T ValueType;
valarray<ValueType>* v_; //!< Valarray object used to store the
matrix elements by rows
size_t m_; //!< Unsigned integer used to
store the number of rows
size_t n_; //!< Unsigned integer used to
store the number of columns

public:
// matrix constructors
Matrix() : m_(), n_(), v_(NULL) {}
explicit Matrix(size_t n, ValueType val = 0) : m_(n), n_(n),
v_(new valarray<ValueType>(val, n*n)) {}
Matrix(size_t m, size_t n, ValueType val = 0) : m_(m), n_(n),
v_(new valarray<ValueType>(val,m*n)){}
Matrix(const Matrix& prototype) {
// copy number of rows and columns from matrix A
m_ = prototype.m_;
n_ = prototype.n_;
// allocates memory for storing the matrix elements
v_ = new valarray<ValueType>(*(prototype.v_));
}
Matrix& operator=(const Matrix& prototype) {
// check to avoid self-copy
if(this != &prototype)
{
// deletes dynamically allocated memory
delete v_;
// copy number of rows and columns from matrix A
m_ = prototype.m_;
n_ = prototype.n_;
// allocates memory for storing the matrix elements
v_ = new valarray<ValueType>(*(prototype.v_));
}
return *this;
}
...
...
...
Now, as you can see the second and third constructor calls are
ambiguous.

Only when T is size_t. Otherwise, you can remove the ambiguity
by casting to the exact type, e.g.:

Matrix< double > m1( static_cast< size_t >( 10 ),
static_cast< size_t >( 20 ) ) ;
// uses first ctor.
Matrix< double > m2( static_cast< size_t >( 10 ),
0.0 ) ; // uses second.

Not really very user friendly, however (and it still fails for
Matrix< size_t >). There are several solutions.

The first thing to remark is that you have violated a basic rule
of overloading. If you overload for more than one arithmetic
type, always (without exception) overload for int as well, since
that's the type of 0, 1, etc. So you should really have four (or
more) constructors:

explicit Matrix(size_t n, ValueType val = 0) ;
explicit Matrix(int n, ValueType val = 0) ;
Matrix(size_t m, size_t n, ValueType val = 0) ;
Matrix(int m, int n, ValueType val = 0) ;
// and possibly...
Matrix(int m, size_t n, ValueType val = 0) ;
Matrix(size_t m, int n, ValueType val = 0) ;

Of course, this will mean that the user will have to cast to an
exact type if he actually has a long. And it will still be
ambiguous if T is size_t (whatever that happens to be) or int.
So this might be a case where the basic rule is wrong.

Another solution---the one I generally use in such cases---is to
define a disambiguating type. I have this globally defined,
since it is useful in many classes:

enum InitWith { initWith } ;

You then provide the constructors:

explicit Matrix( size_t n ) ;
Matrix( size_t n, InitWith, ValueType val ) ;
Matrix( size_t m, size_t n ) ;
Matrix( size_t m, size_t n, InitWith, ValueType val ) ;

The user then writes:

Matrix< double > d1( 10, initWith, 3.14159 ) ;
Matrix< double > d2( 10, 20, initwith, 42 ) ;

and so on.

(There are numerous variants on this. You can create a wrapper
class, so the user writes:
Matrix< double > d( 10, InitValue( 42) ) ;
for example. It's a bit trickier to make this work with the
automatic conversions, however.)

(FWIW: this technique was widely used before "explicit", to
ensure that constructors couldn't be used for conversion. For
example, you're explicit constructor might have a signature:
Matrix<>::Matrix( Dimension, size_t n ) ;
where Dimension was "enum Dimension { dim } ;". The user then
had to write:
Matrix< double > d( dim, 20 ) ;
but this was considered better than allowing the implicit
conversion.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top