Problem with abstract base class

J

Jef Driesen

Suppose I have an abstract base class that looks like this:

template <typename T>
class base {
public: // Typedefs
typedef double value_type;
typedef std::size_t size_type;
public:
// Assignment operators.
virtual base<T>& operator=(const base<T>& rhs) = 0;
virtual base<T>& operator=(const value_type& rhs) = 0;
// Indexing operators.
virtual value_type& operator()(size_type i, size_type j) = 0;
virtual const value_type& operator()(size_type i, size_type j) const
= 0;
// Size functions.
virtual size_type size() const = 0;
virtual size_type rows() const = 0;
virtual size_type columns() const = 0;
// Elementwise mathematical operators
virtual base<T>& operator+=(const base<T>& rhs) = 0;
virtual base<T>& operator-=(const base<T>& rhs) = 0;
virtual base<T>& operator*=(const base<T>& rhs) = 0;
virtual base<T>& operator/=(const base<T>& rhs) = 0;
virtual base<T>& operator+=(const value_type& rhs) = 0;
virtual base<T>& operator-=(const value_type& rhs) = 0;
virtual base<T>& operator*=(const value_type& rhs) = 0;
virtual base<T>& operator/=(const value_type& rhs) = 0;
};

and a derived class:

template <typename T>
class image : public base<T> {
protected: // Data members
value_type *m_data;
size_type m_rows, m_columns;
public:
// Constructors and destructor.
image();
image(size_type m, size_type n);
image(size_type m, size_type n, const value_type& init);
image(const image<T>& rhs);
~image();
// Assignment operators.
image<T>& operator=(const base<T>& rhs);
image<T>& operator=(const value_type& rhs);
// Indexing operators.
value_type& operator()(size_type i, size_type j);
const value_type& operator()(size_type i, size_type j) const;
// Size functions.
size_type size() const;
size_type rows() const;
size_type columns() const;
// Elementwise mathematical operators
image<T>& operator+=(const base<T>& rhs);
image<T>& operator-=(const base<T>& rhs);
image<T>& operator*=(const base<T>& rhs);
image<T>& operator/=(const base<T>& rhs);
image<T>& operator+=(const value_type& rhs);
image<T>& operator-=(const value_type& rhs);
image<T>& operator*=(const value_type& rhs);
image<T>& operator/=(const value_type& rhs);
};

When i want to use the assignment operator in my code, even a simple
program won't compile:

int main(void)
{
image<double> im1(512,512), im2(512,512);
im1 = im2;
return 0;
}

With VC7.1 I receive this error message:

main.obj : error LNK2019: unresolved external symbol "public: virtual
class image::base<double> & __thiscall
image::base<double>::eek:perator=(class image::base<double> const &)"
(??4?$base@N@image@@UAEAAV01@ABV01@@Z) referenced in function "public:
class image::image<double> & __thiscall
image::image<double>::eek:perator=(class image::image<double> const &)"
(??4?$image@N@image@@QAEAAV01@ABV01@@Z)
 
V

Victor Bazarov

Jef said:
Suppose I have an abstract base class that looks like this:

template <typename T>
class base {
public:
// Assignment operators.
virtual base<T>& operator=(const base<T>& rhs) = 0; [...]
};

and a derived class:

template <typename T>
class image : public base<T> {
public:
// Assignment operators.
image<T>& operator=(const base<T>& rhs); [...]
};

When i want to use the assignment operator in my code, even a simple
program won't compile:

It won't *link*. It compiles apparently fine.
int main(void)
{
image<double> im1(512,512), im2(512,512);
im1 = im2;
return 0;
}

With VC7.1 I receive this error message:

main.obj : error LNK2019: unresolved external symbol "public: virtual
class image::base<double> & __thiscall
image::base<double>::eek:perator=(class image::base<double> const &)"
(??4?$base@N@image@@UAEAAV01@ABV01@@Z) referenced in function "public:
class image::image<double> & __thiscall
image::image<double>::eek:perator=(class image::image<double> const &)"
(??4?$image@N@image@@QAEAAV01@ABV01@@Z)

This is a FAQ. Search for "link error templates".

V
 
J

Jef Driesen

Victor said:
Jef said:
Suppose I have an abstract base class that looks like this:

template <typename T>
class base {
public:
// Assignment operators.
virtual base<T>& operator=(const base<T>& rhs) = 0;
[...]

};

and a derived class:

template <typename T>
class image : public base<T> {
public:
// Assignment operators.
image<T>& operator=(const base<T>& rhs);
[...]

};

When i want to use the assignment operator in my code, even a simple
program won't compile:


It won't *link*. It compiles apparently fine.

Ok, my mistake. But this doesn't solve my problem :)
This is a FAQ. Search for "link error templates".

I don't think the problem is related to templates. Both the definition
and the implementation are in the header file. And I know how to use
explicit instantiation otherwise.

Even if I remove all the template stuff (and replace 'T' with 'double'
where appropriate), the link error remains.

class base {
public:
virtual base& operator=(const base& rhs) = 0;
};

class derived : public base {
public:
derived& operator=(const base & rhs){return *this;}
};

int main(void)
{
derived a,b;
a = b;
return 0;
}

I'm not sure, but maybe it has something to do with a compiler generated
assignement operator?
 
J

Jef Driesen

YangWeiQin said:
That's because you just declare the operator=(...), not define it.

I only posted the declaration, but in my actual code the implementation
is provided. And it looks like this:

template<typename T>
image<T>&
image<T>::eek:perator=(const base<T>& rhs)
{
for (size_type i = 0; i < m_rows; ++i) {
for (size_type j = 0; j < m_columns; ++j) {
operator()(i,j) = rhs(i,j);
}
}
return *this;
}
 
D

Donovan Rebbechi

Even if I remove all the template stuff (and replace 'T' with 'double'
where appropriate), the link error remains.

class base {
public:
virtual base& operator=(const base& rhs) = 0;
};

class derived : public base {
public:
derived& operator=(const base & rhs){return *this;}
};

int main(void)
{
derived a,b;
a = b;
return 0;
}

I'm not sure, but maybe it has something to do with a compiler generated
assignement operator?

Looking into this -- if you provide a definition for base::eek:perator=, then
that version gets called. Hmmmm ... very interesting. So let's read the
standard ...

So when you call a = b, what's happening is that the compiler-generated
copy-assignment is called.

The compiler-generated copy-assign calls the base class version of the
assignment operator, which is why you need to define it.

Which is very interesting, but a little beside the point -- I'm more
interested in why you're writing a virtual assignment operator in the first
place -- because this nearly always indicates some design problem. You
generally can't write assignment polymorphically (because you can't assign
an object of one type to a different type). Usually one implements polymorphic
assignment by using (preferably wrapped) pointers.

Cheers,
 
J

Jef Driesen

Donovan said:
Looking into this -- if you provide a definition for base::eek:perator=, then
that version gets called. Hmmmm ... very interesting. So let's read the
standard ...

So when you call a = b, what's happening is that the compiler-generated
copy-assignment is called.

The compiler-generated copy-assign calls the base class version of the
assignment operator, which is why you need to define it.

Which is very interesting, but a little beside the point -- I'm more
interested in why you're writing a virtual assignment operator in the first
place -- because this nearly always indicates some design problem. You
generally can't write assignment polymorphically (because you can't assign
an object of one type to a different type). Usually one implements polymorphic
assignment by using (preferably wrapped) pointers.

I want to have multiple image-like classes (e.g. normal image,
subimage,...). And it should be possible to copy the pixels from one
image type to an image of another type. An example:

image im1(512,512), im2(256,256) // Create two images
range r(0,256);
subimage sub(im,r,r) // Create a subimage
im2 = sub; // Copy pixels from subimage
 
D

Donovan Rebbechi

I want to have multiple image-like classes (e.g. normal image,
subimage,...). And it should be possible to copy the pixels from one
image type to an image of another type. An example:

image im1(512,512), im2(256,256) // Create two images
range r(0,256);
subimage sub(im,r,r) // Create a subimage
im2 = sub; // Copy pixels from subimage

That's not an example of polymorphic assigment. You don't need
a virtual assignment operator to do that.

You should be able to implement it via the base class assignment operator
along these lines:

base& operator=(const base& x) {
image tmp(x.width(),x.height()); // make it exception safe
for (int i = 0; i < width(); ++i)
for (int j = 0; j < height(); ++j )
tmp.setPixel(i,j,x.getPixel(i,j));
swap(tmp);
}

You want to make sure you're not slicing though. Places where you think you
"need" a virtual assignment operator are also places where you're likely to
slice.

Cheers,
 
D

Donovan Rebbechi

That's not an example of polymorphic assigment. You don't need
a virtual assignment operator to do that.

You should be able to implement it via the base class assignment operator
along these lines:

base& operator=(const base& x) {
image tmp(x.width(),x.height()); // make it exception safe
for (int i = 0; i < width(); ++i)
for (int j = 0; j < height(); ++j )
tmp.setPixel(i,j,x.getPixel(i,j));
swap(tmp);
}

Actually, I think I'm starting to see what the problem is (one of them anyway).
If your subimage class references but doesn't own memory, then you can't
allocate new memory for it. But with a subimage, you can't even "assign" to it
in this sense, unless you're assigning an image that has the same dimensions as
the subimage.

So that raises the question -- does your "polymorphic assignment" resize and
copy, or does it require the precondition
width()==rhs.width && height()==rhs.height())
? It's a bad idea to have a polymorphic function do two substantially
different things for different types. For example, what does this do ?
void foo (base& lhs, base& rhs ) { lhs = rhs; }

My suggestion would be to use assignment in the usual idiomatic C++ way:
primarily as a copy-assign, secondarily as a conversion function.

Write a separate function to handle *element-wise* assignment, and make that
function require that image dimensions match.

Cheers,
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top