Undefined reference error.

In this main I have omitted the class Of quiz which Reads Quiz from the File and use it for this application.
Then I stored these questions in the vector of Quiz which then I created a Vector Template Class which is intended to shuffle this quiz now I am wondering how can I use this main below in the Quiz class to sort the Quiz without getting this error
undefined reference to `Vector<Quiz>::Vector()'

But when I use this method in the Vector it works seems the problem is with using a Template class on another class ut says undefined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

int main(){

    ifstream ifs("example.txt");
    if (!ifs)
        return (cout << "Cannot open file\n"), 1;

    vector<Quiz> quizzes;
    Quiz qu;
    LoadQuestions(ifs,quizzes);

  for (const auto& q : quizzes)
        cout << q << '\n';


    Vector<Quiz> myQuiz;
    myQuiz.shuffle();
    for (int i = 0; i < myQuiz.size(); ++i) {
        std::cout<<myQuiz.get(i);
    }


 int correct {0};
    for (Quiz &q : quizzes) {
        cout << "-----------------------------------\n";
        int answer = q.ask();
        if (answer == q.getCorrect()) {
            cout << "Correct!\n";
            ++correct;
        } else {
            cout << "I'm sorry. That answer is incorrect. The correct answer is " << q.getCorrect()+1 << '\n';
        }
    }
   //This is the line that brings me the error by using this Vector
    Vector<Quiz> quiz(quizzes);
    for (int i = 0; i < quiz.size(); ++i) {
        myQuiz.get(i).getQuestion();
    }

 cout << '\n';
    cout << "You answered " << correct << " out of "
         << quizzes.size() << " questions correctly\n";
    
}



Last edited on
It looks like you're attempting to call a vector constructor with a parameter type that it has no idea what to do with it.
It looks like the error is referring to a default constructor which hasn't been defined.

Once a constructor that takes arguments has been provided, the default constructor is no longer available by default. It has to be provided.
You have a mix of Vector<Quiz> and vector<Quiz>. You've probably mis-typed.
There is nothing named Vector.
Sorry, I may not have explained well the Vector is the template that I have created for myself. Just to use it to shuffle the Quiz Object that has the questions read from a text file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
template<typename ValueType>
Vector<ValueType>::Vector(size_t n, ValueType value) : capacity(n), count(n), elements(new ValueType[n]) {
    std::fill_n(elements, n, value);
}

template <typename ValueType>

void Vector<ValueType>::shuffle() {
    srand(time(0));
    for (size_t i = 0; i < count; ++i) {
        size_t j = ( i + std::rand() ) % count;
        if (i != j)
            std::swap(elements[i], elements[j]);
    }
}

template <typename ValueType>
ValueType& Vector<ValueType>::get(size_t index) const {
    checkIndex(index, 0, count - 1, "get");
    return elements[index];
}

template <typename ValueType>
void Vector<ValueType>::checkIndex(size_t index, size_t min, size_t max, const std::string& prefix) const {
    if (index < min || index > max) {
        std::ostringstream out;
        out << "Vector::" << prefix << ": index of " << index << " is outside of valid range ";
        if (size() < 1)
            out << " (empty vector)";
        else {
            out << "[";

            if (min < max)
                out << min << ".." << max;
            else if (min == max)
                out << min;

            out << "]";
        }

        std::cout << " !Error! " << out.str() << '\n';
    }
}
I think seeplus already addressed the cause of the error.
Still not seeing a Vector constructor that takes a vector<Quiz> as a parameter... id just like to say that this confusion is created bc of the poor naming scheme and the fact that this is unnecessarily overly complicated.
Last edited on
1
2
3
4
5
6
7
8
9
template <typename ValueType>
Vector<ValueType>::Vector(const std::vector<ValueType>& v){
    count = v.size();
    capacity = v.size();
    elements = new ValueType[count];
    for (int i = 0; i < count; i++) {
        elements[i] = v[i];
    }
}


I have included the Constructor still the error is persistence as Long I am using this Template on another class by defining its header file. But if I just add the Main and use other class it does work but now that is problematic as other class I have designed them to have more methods and as they should be the ones having the main method.
As seeplus already pointed out, You don't have an implementation for the default constructor for Vector. But you also use vector in line 8.

It looks like you've included vector (#include <vector>), when you shouldn't have.
Last edited on
I been playing around with this. I basically have a working program. Take a look at what I got and maybe it will help you. I've tested it in a variety of ways and seems to be working halfway decently. the answer comparison to the accepted answers is not the greatest but it gets the point across. The file I used just basically had a question followed by the answer on a new line. This and the answer comparisons could all be tightened up via stricter formatting. Im just not that worried about it. The parts about the file can be commented out and just use the 5 questions or so i have in the test section of main().

https://pastebin.com/ajSEAfT3
Last edited on
On something like:
1
2
3
    string get_q() {
        return this->q;
    }


You shouldn't have get/set methods. Think of what the object should be doing and express the methods in those terms.

You don't need this->, it's implied. Java yes, C++ no.

Think about whether the method is logically changing its state. If not, mark it const.

Using q for question and a for answer doesn't save you any work.
On:
1
2
3
    bool operator==(string a) {
        return this->compare(a);
    }

You should pass the parameter by const reference, and it should be const. It may not matter here, but if you start doing mixing your code with stuff from the standard library, or libraries, it'll matter.

Input/Output shouldn't be in your methods. If you find yourself doing that, you're headed in the wrong direction.

For example, if you call Rectangle::Area(), you wouldn't expect it to prompt your for dimensions, you'd expect it to know that already.
@kbw if those are your only complaints then I guess I'm doing OK. The getters and setters were just thrown in there by reflex. If I decided to make those variables private in the future those methods would serve a purpose.

I'm not sure how to feel about your statement about input or output in a method. Not sure how this is any different than overloading the bitshift left operator which is fairly common. The reason to prompt to an answer in the question struct is bc its part of the game. I guess it could be moved to the quiz struct but I wasn't splitting hairs on this thing. I barely even thought about the hierarchy of things and just kinda let it flow. This isn't my project. I just saw this dude struggling and figuring I'd pump out something similar to what he was trying to do. I just try to keep as much functionality wrapped up inside my objects. That way the main stays as clean as possible. I just think my code is more readable that way. Clearly it's more readable than what was originally posted in this thread. Lol.
The comments are meant to be helpful guides, not rules to live by. So take what you find useful, and feel free to ignore things you find irrelevant or just plain wrong.

On I/O in methods, I often see code like:
1
2
3
4
5
6
7
MoneyType Employee::CalculatePay() {
    int hoursWorked;
    cout << "Enter hours worked" << endl;
    cin >> hoursWorked;

   // ... do some stuff and return a value
}

It's not code I'm generally happy with. It's kinda like:
1
2
3
4
5
6
7
double Rectangle::Area() const {
    double length, height;
    cout << "Enter height and width" << endl;
    cin >> length >> height;

    return length * height;
}

You should instinctively think that there's something wrong with this.

For example, you'd expect to be able to write:
 
    auto len = name.length();

without worrying if it'll prompt you for some kind of input.

On the particular matter of the structure of the code:
* Question could be a struct with question/answer strings, and no methods.
* Quiz is a set of questions, and answers to the questions. You could implement as:
 
std::vector<std::pair<Question, std::string>> questions; // questions and player answers 


The main app would:
* Initialize a particular Quiz with a random subset of Questions from some larger set.
* Ask the player one question at a time and get the player answer.
* At the end, match player answers with the fixed answers in Question and generate a score.
I'm not upset about it or anything. Don't sweat it. The examples of code you posted I absolutely take issue with.

Bare minimum I believe that the question struct should have a comparison method and overload. The reason I started adopting this type of style is so that I can put things inside nested containers and not have to try so hard to get the stuff back out.

The q and a in an out could definitely be moved to the quiz struct utilizing the question comparison operator determine if the answer is correct or not sure. in a game that is entirely based on i/o I definitely believe that there's no good reason to not include those aspects into the struct itself. The way it's setup up how the end goal could be accomplished in a variety of ways. As I was writing I wasn't sure exactly how that would be. You'd definitely want a file to just input questions and select them at random. I just added overloads to add_question for testing etc. I'm all over the place in this response. I just like my structs to be damn near automatic. Like once it's populated with the appropriate data to call basically one function and run from start to finish. I also like to leave my options open. That's partly why I have some unnecessary methods in there that might not be being used.

The vector pair you have posted really doesn't seem to fit in bc theres no good reason to store the players answers. It's all for fun, this is just a hobby for me. I just like to stay busy. I been working on sort algorithms for fun lately. I'm trying to see if I can come up with something that rivals std sort. Wall of text sorry. Hijacked thread sry lol.
Last edited on
@markyrocks. Your container has some issues. Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <vector>
#include <algorithm>
#include <iostream>
#include <initializer_list>
#include <cstdlib>
#include <ctime>

template <typename T>
class container {
	size_t size_ {};
	T* tainer {};

public:
	container() {}
	container(size_t s) : tainer(new T[s]), size_(s) {}
	~container() { delete[] tainer; }

	container(const container& c) : size_(c.size_), tainer(new T[c.size_]) {
		std::copy_n(c.tainer, size_, tainer);
	}

	container(const std::initializer_list<T>& il) : size_(il.size()), tainer(new T[il.size()]) {
		std::copy(il.begin(), il.end(), tainer);
	}

	container(container&& c) { swap(c); }
	container& operator=(const container& c) { auto c1 {c}; swap(c1); return *this; }
	container& operator=(container&& c) { swap(c); return *this; }

	void swap(container& c) {
		std::swap(c.size_, size_);
		std::swap(c.tainer, tainer);
	}

	container(const std::vector<T>& v) : size_(v.size()), tainer(new T[v.size()]) {
		std::copy_n(v.begin(), size_, tainer);
	}

	container(size_t s, const T& val) : size_(s), tainer(new T[s]) {
		std::fill_n(tainer, s, val);
	}

	void shuffle() {
		for (auto a {rand() % 100}; a; --a)
			std::swap(tainer[rand() % size_], tainer[rand() % size_]);
	}

	T& operator[](size_t index) { return tainer[index]; }
	const T& operator[](size_t index) const { return tainer[index]; }

	void push_back(const T& val) {
		const auto c {new T[size_ + 1]};

		std::copy_n(tainer, size_, c);
		c[size_++] = val;
		delete[] tainer;
		tainer = c;
	}

	size_t size() const { return size_; }
};

template<typename T>
std::ostream& operator<<(std::ostream& os, const container<T>& c) {
	for (size_t i = 0; i < c.size(); ++i)
		os << c[i] << ' ';

	return os;
}

int main()
{
	srand(static_cast<unsigned>(time(nullptr)));

	container<int> c {1, 2, 3};

	c.push_back(4);
	c.push_back(5);
	c.push_back(6);
	c.shuffle();
	std::cout << c << '\n';

	const auto c1 {c};

	std::cout << c1 << '\n';

	container<int> c2;

	c2 = container<int> {10, 11, 12};

	std::cout << c2 << '\n';

	const auto c3 {container<int>{5, 6, 7, 8}};

	std::cout << c3 << '\n';

	const container c4(10, 34);

	std::cout << c4 << '\n';
}

Last edited on
@seeplus nice. I've been thinking about the issues with the container. It had definitely crossed my mind to make a copy constructor and etc. That initializr list is super sweet. Haven't seen that b4. I'm kinda curious if it would break if you initialize a container of one type and then try to set it equal to another type. I guess you did do the = operator overload. Didn't see that until now. Very nice.
I'm kinda curious if it would break if you initialize a container of one type and then try to set it equal to another type.


It sounds like you don't fully comprehend template classes.

container<int> and container<double> and container<std::string> are all completely different types. Just like you cannot assign a string to an int variable, you cannot assign a container<string> to a container<int> variable.

With POD (plain old data) types, values can be cast from one type to another (double -> int, etc.), but that does not apply to more complex classes unless a specific assignment or cast operator is written to allow it.
Topic archived. No new replies allowed.