I have a function that works fine when I created it in main. I moved it to a implementation file, and placed the the prototype in a template class. Now I am getting an error under the function call:
Severity Code Description Project File Line Suppression State
Error (active) E0020 identifier "noDuplicates" is undefined Project29
I think I may have something wrong with the syntax in the definition and prototype but I'm not sure. I am learning templates and how the files work with them so this is all new and I am trying to understand it.
template<class ItemType>
class ArraySet : public SetInterface<ItemType>
{
private:
constint END = 6;
staticconstint DEFAULT_CAPACITY = 6; // Small size to test for a full bag
ItemType items[DEFAULT_CAPACITY]; // Array of bag items
int itemCount; // Current count of bag items
int maxItems; // Max capacity of the bag
public:
ArraySet();
ArraySet& noDuplicates(const ArraySet<ItemType>& set, std::string items[]);
};
#include "ArraySet.cpp" // Pound include here so I can remove ArraySet.cpp from compiler.
// ArraySet.cpp is in different file with the same project. Removed from the Solution Explorer though in Visual Studio.
template<class ItemType>
ArraySet<ItemType>& ArraySet<ItemType>::noDuplicates(const ArraySet<ItemType>& set, std::string items[])
{
for (int index = 0; index < END; index++)
{
std::string& firstItem = items[index];
for (int nextIndex = index + 1; nextIndex < END; nextIndex++) {
if (firstItem == items[nextIndex])
set.remove(items[nextIndex]);
}
}
}
// Function in client program.
void setTester(ArraySet<string>& set)
{
cout << "isEmpty: returns " << set.isEmpty()
<< "; should be 1 (true)" << endl;
displaySet(set);
std::string items[] = { "one", "two", "three", "four", "five", "one" };
cout << "Add 6 items to the set: " << endl;
for (int i = 0; i < END; i++)
{
set.add(items[i]);
} // end for
displaySet(set);
noDuplicates(set, items); // ERROR here .....................................................
cout << "After removing any duplicate values." << endl;
displaySet(set);
}
// main
int main()
{
ArraySet<std::string> set;
cout << "Testing the Array-Based set:" << endl;
cout << "The initial set is empty." << endl;
setTester(set);
Oh okay. So I would need to create an object of the class and then call it with the object. Is that correct? This is what I just tried but didn't get it right.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
void setTester(ArraySet<string>& set)
{
ArraySet callFunction;
cout << "isEmpty: returns " << set.isEmpty()
<< "; should be 1 (true)" << endl;
displaySet(set);
std::string items[] = { "one", "two", "three", "four", "five", "one" };
cout << "Add 6 items to the set: " << endl;
for (int i = 0; i < END; i++)
{
set.add(items[i]);
} // end for
displaySet(set);
callFunction.noDuplicates(set, items);
}
Error:
Severity Code Description Project File Line Suppression State
Error (active) E0441 argument list for class template "ArraySet" is missing Project29
Severity Code Description Project File Line Suppression State
Error (active) E1776 function "ArraySet<ItemType>::operator=(const ArraySet<std::string> &) [with ItemType=std::string]" (declared implicitly) cannot be referenced -- it is a deleted function
So I would need to create an object of the class and then call it with the
object.
Why would you need to create another object when you already have one?
1 2 3 4 5 6
void setTester(ArraySet<string>& set) {
/* Your parameter 'set' is already an object, isn't it? So why not just call
* it with that same object? */
set.noDuplicates(set, items); // like this
}
In your noDuplicates function, you have the first parameter is an object of the
class. You should not have to do that, since functions of the class already
have access to the object via the keyword this.
Instead you can implement it like this:
1 2 3 4 5 6 7 8 9 10 11 12 13
template<class ItemType>
ArraySet<ItemType>& ArraySet<ItemType>::noDuplicates(std::string items[])
{
for (int index = 0; index < END; index++)
{
std::string& firstItem = items[index];
for (int nextIndex = index + 1; nextIndex < END; nextIndex++) {
if (firstItem == items[nextIndex])
this->remove(items[nextIndex]);
}
}
}
You can either do that, or make the noDuplicates function in the class static. This elimates the need to create an object just to access
the function.
1 2 3 4 5
class ArraySet: public SetInterface<ItemType> {
/* ... */
public:
static ArraySet& noDuplicates(const ArraySet<ItemType>& set, std::string items[]);
};
EDIT:
stoneJax wrote:
Error:
Severity Code Description Project File Line Suppression State
Error (active) E0441 argument list for class template "ArraySet" is missing Project29
You're getting this because this line:
1 2 3 4 5 6 7
ArraySet callFunction;
/* You're not giving a data type to the object. Remember you implement the
* class with a template, so you have to do
*
* ArraySet<TYPE> someObject;
* */
Thank you, you hit on a lot of good points that makes sense. I got rid of the the extraneous object and object parameter. I called noDuplicates with the set object, and used the this pointer like you mentioned. However, I get this error:
Severity Code Description Project File Line Suppression State
Error C4716 'ArraySet<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >::noDuplicates': must return a value
I tried returning the function with "return this;" and "return items;" but that didn't work.
// Client file
template<class ItemType>
void setTester(ArraySet<ItemType>& set)
{
cout << "isEmpty: returns " << set.isEmpty()
<< "; should be 1 (true)" << endl;
displaySet(set);
std::string items[] = { "one", "two", "three", "four", "five", "one" };
cout << "Add 6 items to the set: " << endl;
for (int i = 0; i < END; i++)
{
set.add(items[i]);
} // end for
displaySet(set);
set.noDuplicates(items); // Corrected by calling with set object.
cout << "After removing any duplicate values." << endl;
displaySet(set);
}
// Implementation file
template<class ItemType>
ArraySet<ItemType>& ArraySet<ItemType>::noDuplicates(std::string items[]) // Got rid of extra object parameter.
{
for (int index = 0; index < END; index++)
{
std::string& firstItem = items[index];
for (int nextIndex = index + 1; nextIndex < END; nextIndex++) {
if (firstItem == items[nextIndex])
this->remove(items[nextIndex]);
}
}
}
Severity Code Description Project File Line Suppression State
Error C4716 'ArraySet<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >::noDuplicates': must return a value
I should note that making these changes affected some things in main that were working fine before. Things meaning calls to other functions which are assigned to objects. I get an error at the assignment operator. I figure that we focus on one thing (above) at a time though, so I don't want to overload with secondary errors.
You do not need to return a value. Make it void unless you really do want to use the returned data for something. In this case, I don't really see that you need to.
Oh okay. Awesome, thank you, that simplifies things and noDuplicates seems to be working now.
Unfortunately, these changes created some problems in main. I can't make sense of it though because these changes seem to be independent from any of the other functions. I'll post my main below with the error. The errors show up where all the = signs are.
int main()
{
bool returnFromUnion;
constint LIMIT = 3;
ArraySet<std::string> set1;
ArraySet<std::string> set2;
ArraySet<std::string> set3;
ArraySet<std::string> intersection;
ArraySet<std::string> difference;
ArraySet<std::string> set;
cout << "Testing the Array-Based set:" << endl;
cout << "The initial set is empty." << endl;
setTester(set);
std::string items1[] = { "one", "two", "three" };
cout << "Add 3 items to the set1: " << endl;
for (int i = 0; i < LIMIT; i++)
{
set1.add(items1[i]);
} // end for
std::string items2[] = { "four", "five", "six" };
cout << "Add 3 items to the set2: " << endl;
for (int i = 0; i < LIMIT; i++)
{
set2.add(items2[i]);
} // end for
cout << "\nset1: " << endl;
displaySet(set1);
cout << "set2: " << endl;
displaySet(set2);
set3 = set1.setUnion(set2, returnFromUnion);
if (returnFromUnion)
{
cout << "The union is: " << endl;
displaySet(set3);
}
else cout << "The Union overflowed." << endl;
intersection = set1.setIntersection(set2);
cout << "The intersection is: " << endl;
displaySet(intersection);
set2.clear();
cout << "Changing set2 to further test setIntersection." << endl;
std::string changeItems2[] = { "one", "five", "three" };
cout << "Add 3 items to the set2: " << endl;
for (int i = 0; i < LIMIT; i++)
{
set2.add(changeItems2[i]);
}
cout << "\nset2: " << endl;
displaySet(set2);
intersection = set1.setIntersection(set2);
cout << "The intersection is: " << endl;
displaySet(intersection);
difference = set1.setDifference(set2);
cout << "The difference is: " << endl;
displaySet(difference);
cout << "All done!" << endl;
return 0;
} // end main
Severity Code Description Project File Line Suppression State
Error (active) E1776 function "ArraySet<ItemType>::operator=(const ArraySet<std::string> &) [with ItemType=std::string]" (declared implicitly) cannot be referenced -- it is a deleted function
template<class ItemType>
class ArraySet : public SetInterface<ItemType>
{
private:
constint END = 6;
staticconstint DEFAULT_CAPACITY = 6; // Small size to test for a full bag
ItemType items[DEFAULT_CAPACITY]; // Array of bag items
int itemCount; // Current count of bag items
int maxItems; // Max capacity of the bag
// Returns either the index of the element in the array items that
// contains the given target or -1, if the array does not contain
// the target.
int getIndexOf(const ItemType& target) const;
public:
ArraySet();
void noDuplicates(std::string items[]);
ArraySet& setUnion(const ArraySet& set2, bool& failure);
ArraySet& setIntersection(const ArraySet& set2);
ArraySet& setDifference(const ArraySet& set2);
int getCurrentSize() const;
bool isEmpty() const;
bool add(const ItemType& newEntry);
bool remove(const ItemType& anEntry);
void clear();
bool contains(const ItemType& anEntry) const;
std::vector<ItemType> toVector() const;
}; // end ArrayBag
In several place like line 36, 45, etc. you try to copy the result of that functions (setUnion(...), setIntersection(...), etc.) to ArraySet. This is not allowed due to the constant member END.
By the way: setUnion(...), setIntersection(...), setDifference(...) return a non const reference ArraySet&. This is impossible without a global/static variable which is most likely not correct.
so they are pointer objects then the function calls will work correctly? I'm confused because these objects and functions were working fine before I changed noDuplicates.
@ coder777, not sure what you mean here: "By the way: setUnion(...), setIntersection(...), setDifference(...) return a non const reference ArraySet&. This is impossible without a global/static variable which is most likely not correct."
I commented out the constant member END, and hard coded a 6 in the for loop for the noDuplicates function and everything seems to work. However, I have been told (in school) that hard coding values is not good programming practice, and I'm not supposed to do that.
Actually I just added static to the constant member END and that seems to have done the trick. Code is compiling with expected results. Do you guys feel that I am going about it the best way?
Instead of static you can use enum: enum { END = 6, DEFAULT_CAPACITY = 6 };
Do you guys feel that I am going about it the best way?
How ist it possible that those functions return ArraySet& (as a reference)?
If you modify the object itself why whould you want to copy it to another set?
> How ist it possible that those functions return ArraySet& (as a reference)? return *this;
> If you modify the object itself why whould you want to copy it to another set?
method chaining
and you copy because you want a copy
¿do you consider this error-prone or non-intuitive?
@OP:
1 2 3
intersection = set1.setIntersection(set2);
//here `set1' has the intersection of both sets, ¿is that what you want?
difference = set1.setDifference(set2);