Hydra-
Hydra-

Reputation: 1

LNK2019 with Template

So, I'm having an issue with linking. The answer is probably dead-weight easy, but I guess I'm bonked. I define a class for calculating things that takes a stringstream.

Relevant section of header file:

#include <sstream>
using namespace std;
template<class T>
class Finder {
public:
    Finder(istringstream& input) {};
    ~Finder() {};

    template<typename T> Finder(T& input) {};
    template<typename T> ~Finder() {};

    T check(istringstream&);

    template<typename T> friend ostream& operator << (ostream&, Finder<t>&);
};

template<class T>
T Finder<T>::check(istringstream& input)

And then my driver file to the last call:

#include <sstream>
#include <string>
#include <iostream>
#include "Finder.h"

using namespace std;

int main(int argc, char** argv) {
        Finder<int> thing;

        string expression;
            getline(cin, expression);

            while(expression[0] != 'q') {
        try {
            int result = thing.check(istringstream(expression));

Errors are: 1>driver.obj : error LNK2019: unresolved external symbol "public: __thiscall Finder::Finder(void)" (??0?$Finder@H@@QAE@XZ) referenced in function _main

1>driver.obj : error LNK2019: unresolved external symbol "public: __thiscall Finder::~Finder(void)" (??1?$Finder@H@@QAE@XZ) referenced in function __catch$_main$0

Upvotes: 0

Views: 825

Answers (1)

WhozCraig
WhozCraig

Reputation: 66194

First, don't restrict your input to just string streams. Use the generic std::istream instead unless you have a solid reason to do otherwise. Your class will be more robust and able to source input from multiple source stream types, not just std::istringstream (such as a file stream or the input console).

Second I'm nearly certain this is what you're trying to do:

#include <iostream>

// forward declare class
template<class T>
class Finder;

// forward declare ostream inserter
template<class T>
std::ostream& operator <<(std::ostream&, const Finder<T>&);

// full class decl
template<class T>
class Finder
{
    // friend only the inserter that matches this type, as opposed to
    //  all inserters matching all T for Finder
    friend std::ostream& operator<< <>(std::ostream&, const Finder<T>&)
    {
        // TODO: implement inserter code here
    }

public:
    Finder()
    {
        // TODO: default initialization here
    };

    Finder(const T& value)
    {
        // TODO: initialize directly from T value here.
    }

    Finder(std::istream& input)
    {
        // TODO: initialize from input stream here.
    }

    ~Finder()
    {
        // TODO: either implement this or remove it outright. so far
        //  there is no evidence it is even needed.
    }

    T check(std::istream& input)
    {
        // TODO: implement check code here. currently just returning a
        //  value-initialized T. you will change it as-needed

        return T();
    };
};

A sample usage would be:

int main()
{
    Finder<int> f;

    std::istringstream iss("1 2 3");
    f.check(iss);
}

Notice there is one T, the one that comes from the class template itself. If there is a need for auxiliary types for member functions (or even the constructor) that is doable as well using template member function with a different type name, such as:

template<class T>
class Simple
{
public:
    // a template member constructor
    template<typename U> 
    Simple(const U& val)
    {
    }

    // a regular template member
    template<typename U>
    void func(U value)
    {
    }
};

and invoked like this:

Simple<int> simple(3.1415926); // U will be type double
simple.func("test");           // U will be type const (&char)[5]

Notice with the member function templates, like all function templates, the types are deduced from the parameters passed, not specified (though they could be to force a specific type, we don't here).

Anyway, hope it helps.

Upvotes: 1

Related Questions