I've been trying to figure out why I get an error when I try to build this code in QTCreator. I found some other posts similar to this but I think I have a similar but different issue.
In main.cpp I basically have some Filter (a templated struct with a pure virtual function) that I'm creating that returns to me a vector of MyProducts. I give the filter a Specification (a templated struct with a pure virtual function) and a vector of MyProduct to search through.
But I keep getting this error message during the build.
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112: error: allocating an object of abstract class type 'Specification<MyProduct>'
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112:36: error: allocating an object of abstract class type 'Specification<MyProduct>'
AndSpecification<MyProduct> as(cs, ss);
^
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:29:18: note: unimplemented pure virtual method 'is_satisfied' in 'Specification'
virtual bool is_satisfied(T* item) = 0;
^
Here is my source code
main.cpp
#include <iostream>
#include <vector>
using namespace std;
enum class ColorTable
{
red,
green,
blue
};
enum class SizeTable
{
small,
medium,
large
};
struct MyProduct
{
string name;
ColorTable color;
SizeTable size;
};
template <typename T> struct Specification
{
virtual bool is_satisfied(T* item) = 0;
};
template <typename T> struct Filter
{
virtual vector<T*> filter(vector<T*> items, Specification<T>& spec) = 0;
};
struct BetterFilter : Filter<MyProduct>
{
vector<MyProduct*> filter(vector<MyProduct *> items, Specification<MyProduct> &spec) override
{
vector<MyProduct*> result;
for (auto& item : items)
{
if (spec.is_satisfied(item))
result.push_back(item);
}
return result;
}
};
struct ColorSpecification : Specification<MyProduct>
{
ColorTable color;
ColorSpecification(ColorTable color) : color(color)
{}
bool is_satisfied(MyProduct* item) override
{
if (item->color == color)
return true;
else
return false;
}
};
struct SizeSpecification : Specification<MyProduct>
{
SizeTable size;
SizeSpecification(SizeTable size) : size(size)
{}
bool is_satisfied(MyProduct* item) override
{
if (item->size == size)
return true;
else
return false;
}
};
template <typename T> struct AndSpecification : Specification<T>
{
Specification<T>& first;
Specification<T>& second;
AndSpecification(Specification<T> first, Specification<T> second) : first(first) , second(second)
{}
bool is_satisfied(T* item) override
{
if (first.is_satisfied(item) && second.is_satisfied(item))
return true;
else
return false;
}
};
int main()
{
MyProduct prod_1{"Apple", ColorTable::green, SizeTable::large};
MyProduct prod_2{"Jeans", ColorTable::green, SizeTable::small};
MyProduct prod_3{"Graphics Card", ColorTable::blue, SizeTable::large};
vector<MyProduct*> items = {&prod_1, &prod_2, &prod_3};
BetterFilter bf;
ColorSpecification cs(ColorTable::green);
SizeSpecification ss(SizeTable::large);
AndSpecification<MyProduct> as(cs, ss);
auto green_things = bf.filter(items, cs);
for (auto& item : green_things)
cout << item->name << " is green." << endl;
auto large_things = bf.filter(items, ss);
for (auto& item : large_things)
cout << item->name << " is large.\n\n" << endl;
// auto large_green_things = bf.filter(items, as);
//for (auto& item : large_green_things)
// cout << item->name << " is large and green." << endl;
return 0;
}
I tried to reproduce the OPs issue in coliru.
This is what I got:
main.cpp: In function 'int main()':
main.cpp:112:42: error: cannot allocate an object of abstract type 'Specification<MyProduct>'
112 | AndSpecification<MyProduct> as(cs, ss);
| ^
main.cpp:27:30: note: because the following virtual functions are pure within 'Specification<MyProduct>':
27 | template <typename T> struct Specification
| ^~~~~~~~~~~~~
main.cpp:29:18: note: 'bool Specification<T>::is_satisfied(T*) [with T = MyProduct]'
29 | virtual bool is_satisfied(T* item) = 0;
| ^~~~~~~~~~~~
main.cpp:112:33: warning: unused variable 'as' [-Wunused-variable]
112 | AndSpecification<MyProduct> as(cs, ss);
| ^~
The first thing I noticed: The struct AndSpecification
is broken:
template <typename T> struct AndSpecification : Specification<T>
{
Specification<T>& first;
Specification<T>& second;
AndSpecification(Specification<T> first, Specification<T> second) : first(first) , second(second)
{}
};
The member-variables first
and second
store a reference. However, the initialization in the constructor is done with value arguments. I.e. the members are initialized with local variables which are destroyed after the constructor is left (actually if the full-expression calling the constructor is done).
Concerning the override
, I hadn't a clue, so I fixed this first.
…and got (to my surprise) the following output:
Apple is green.
Jeans is green.
Apple is large.
Graphics Card is large.
Live Demo on coliru
I came to the conclusion that the found/fixed bug and the error message were related but I still fail to explain this. The best I've at hand: If the code contains U.B. no expectations can be made about the outcome.
With the help of @Ayxan Haqverdili, I found out why the error is justified:
Value parameters cause temporary copies in the call. These copies would be of type Specification<T>
but that type is abstract and cannot be instanced.
Your compiler tells you where the error occurs.
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112:36: error: allocating an object of abstract class type 'Specification<MyProduct>' AndSpecification<MyProduct> as(cs, ss); ^
The attempt to instantiate Specification<MyProduct>
occurs when using cs
as the first argument to the AndSpecification<MyProduct>
constructor. Creating cs
was fine, but somehow this function call triggers the construction of an abstract class. So let's look at the declaration of that constructor.
AndSpecification(Specification<T> first, Specification<T> second)
The first parameter is passed by value (unlike many of your other parameters). This means that a newly-constructed Specification<MyProduct>
object is initialized from cs
. (The Specification<MyProduct>
sub-object of cs
is looked at, and those data members are copied to the new object.) However, Specification<MyProduct>
is abstract (as are all Specification<T>
classes), so it cannot be constructed. Hence the error.
Change your constructor to accept arguments by reference.
AndSpecification(Specification<T>& first, Specification<T>& second)
// ^ ^
See? It's not as complicated as you made it appear, if you know how to read the error message.
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