[RESOLVED] template function in non-template class
I am trying to implement BigInteger library by myself. I am storing numbers in std::string.
The header file (BigInt.h) contains:
Code:
#ifndef BIGINT_H
#define BIGINT_H
#include <iostream>
//#include <vector>
/**Specifications:
* Constructor: can take, int, string(formatted as number), float or double
* operators to define +, -, *, /, %, power(), <<, >>
*/
class BigInt
{
public:
/** Default constructor */
BigInt();
/** string constructor */
BigInt(const std::string&);
/** int, float or double constructor */
template<typename T>
BigInt(const T&);
/** Default destructor */
virtual ~BigInt();
/** Copy constructor
* \param other Object to copy from
*/
BigInt(const BigInt& other);
/** Assignment operator
* \param other Object to assign from
* \return A reference to this
*/
BigInt& operator=(const BigInt& other);
/**
* Overloaded << ostream operator
*/
friend std::ostream& operator<<(std::ostream& os, const BigInt& bigInt);
protected:
private:
/**
* Number is represented as:
* +0.123324 or -0.1232432
* +123 or -123 etc
* [+ or -] then [numbers] then maybe [.] then [more numbers]
*/
std::string dec_rep;
};
#endif // BIGINT_H
definition of member functions is in BigInt.cpp:
Code:
#include "BigInt.h"
#include <string>
#include <regex>
//#include <sstream>
BigInt::BigInt()
{
//ctor
dec_rep = "+0";
}
template<typename T>
BigInt::BigInt(const T& n)
{
dec_rep = std::to_string(n);
if( dec_rep[0] != '+' && dec_rep[0]!='-')
{
dec_rep = "+" + dec_rep;
}
}
/**
* it accepts input as string if it is correctly formatted as number
* accepted forms are: 1234, 1.234, +1233, +1.234, 0.234, .234, +.234, -123 etc
* these are unaccepted; --123, +-23, -+32, 0.234.0 or any other wrong format
*/
BigInt::BigInt(const std::string& str)
{
std::regex re("[-+]?([0-9]*\\.[0-9]+)"); //regular expression to check if input is in proper format
std::smatch m;
if(std::regex_match(str, m, re))
{
dec_rep = str;
if((str[0] == '+' || str[0] == '-') && str[1] == '.')
{
dec_rep = dec_rep[0] + "0" + dec_rep.substr(1);
}
else if(str[0] == '.')
{
dec_rep = "+0" + dec_rep;
}
else if(str[0]>='0' && str[0]<='9')
{
dec_rep = "+" + dec_rep;
}
}
}
BigInt::~BigInt()
{
//dtor
}
BigInt::BigInt(const BigInt& other)
{
//copy ctor
}
BigInt& BigInt::operator=(const BigInt& rhs)
{
if (this == &rhs) return *this; // handle self assignment
//assignment operator
return *this;
}
std::ostream& operator<<(std::ostream& os, const BigInt& bigInt)
{
os<<bigInt.dec_rep;
return os;
}
main() is in main.cpp:
Code:
#include <iostream>
#include "BigInt.h"
using namespace std;
int main()
{
BigInt n(1); //Line 9 where 1st error comes
BigInt n_default;
BigInt str_n("1"); //Line 11 where 2nd error comes
cout<<n<<" "<<n_default<<" "<<str_n<<endl;
cout << "Hello world!" << endl;
return 0;
}
When I try to run it, it shows following errors:
obj/Debug/main.o||In function `main':|
1. undefined reference to `BigInt::BigInt<int>(int const&)' on line 9 of main.cpp
2. undefined reference to `BigInt::BigInt<char [2]>(char const (&) [2])' on line 11 of main.cpp
3. error: ld returned 1 exit status
||=== Build failed: 3 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
I'm using code-blocks 16.01 with GCC compiler on Linux Mint 18.2 'Sonya' (64 bit).
When templates are used, you can't separate header from implementation. The implementation has to placed in the header - similar to how the STL classes are implemented.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
When templates are used, you can't separate header from implementation. The implementation has to placed in the header - similar to how the STL classes are implemented.
I moved all code in one file: BigInt.h
But now it gives different errors. It wants to use template function when string argument is passed. But I have already overloaded the function with separate string argument, so why doesn't it use that?
Errors:
1. no matching function for call to ‘to_string(const char [2])’
2. invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
and other errors similar to error 2
It wants to use template function when string argument is passed. But I have already overloaded the function with separate string argument, so why doesn't it use that?
The template function will match any constructor call. Hence it has matched the template with type char[2]. What you need to do is to have an explicit template specialisation(s) for when you don't want the normal template to apply. Consider
Note that this will also apply to the copy constructor.
Last edited by 2kaud; July 17th, 2017 at 01:32 PM.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
But now it gives different errors. It wants to use template function when string argument is passed. But I have already overloaded the function with separate string argument, so why doesn't it use that?
Recall that there is the concept of a null terminated string as inherited from C (i.e., contiguous sequences of char, stored in arrays, terminated by a null character), and then there are string objects with the type of std::string. It looks like the compiler chose the template because the type of the argument was that of a char array rather than a std::string. There may be other solutions, but perhaps one quick solution would be to overload for const char*. This overload can delegate to the constructor that overloads for std::string const reference, e.g., by explicitly constructing a std::string object as the argument as it delegates, so you don't really do extra work.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
BigInt str_n("1"); //Line 11 where 2nd error comes
This is calling the constructor with type const char* and matches the standard template. If you instead use
Code:
BigInt str_n("1"s); //Line 11 where 2nd error comes
then this explicitly makes the parameter of type string which then matches the explicit template specialisation for string. As laserlight suggested in post #5, you might also want to provide a function overload for type const char* (it can't be a template specialisation as the type has to be a ref because the main template uses a ref).
Note that if you use the s string suffix, then you either need to use using name std at the beginning of the program or
#include <iostream>
#include <string>
#include <regex>
//#include <vector>
/**Specifications:
* Constructor: can take, int, string(formatted as number), float or double
* operators to define +, -, *, /, %, power(), <<, >>
*/
class BigInt
{
public:
/** Default constructor */
BigInt();
/** int, float or double constructor */
template<typename T>
BigInt(const T&);
/** string constructor */
template<>
BigInt(const std::string&);
/** char * constructor */
BigInt(const char * const);
/** Default destructor */
virtual ~BigInt();
/** Copy constructor
* \param other Object to copy from
*/
BigInt(const BigInt& other);
/** Assignment operator
* \param other Object to assign from
* \return A reference to this
*/
BigInt& operator=(const BigInt& other);
/**
* Overloaded << ostream operator
*/
friend std::ostream& operator<<(std::ostream& os, const BigInt& bigInt);
protected:
private:
/**
* Number is represented as:
* +0.123324 or -0.1232432
* +123 or -123 etc
* [+ or -] then [numbers] then maybe [.] then [more numbers]
*/
std::string dec_rep;
};
BigInt::BigInt()
{
//ctor
dec_rep = "+0";
}
template<typename T>
BigInt::BigInt(const T& n)
{
dec_rep = std::to_string(n);
if (dec_rep[0] != '+' && dec_rep[0] != '-')
{
dec_rep = "+" + dec_rep;
}
}
template<>
BigInt::BigInt(const std::string& str)
{
std::regex re("[-+]?([0-9]*\\.[0-9]+)");
std::smatch m;
if (std::regex_match(str, m, re))
{
dec_rep = str;
if ((str[0] == '+' || str[0] == '-') && str[1] == '.')
{
dec_rep = dec_rep[0] + "0" + dec_rep.substr(1);
}
else if (str[0] == '.')
{
dec_rep = "+0" + dec_rep;
}
else if (str[0] >= '0' && str[0] <= '9')
{
dec_rep = "+" + dec_rep;
}
}
}
BigInt::BigInt(const char * const cst) : BigInt(std::string(cst)) {}
BigInt::~BigInt()
{
//dtor
}
BigInt::BigInt(const BigInt& other)
{
//copy ctor
}
BigInt& BigInt::operator=(const BigInt& rhs)
{
if (this == &rhs) return *this; // handle self assignment
//assignment operator
return *this;
}
std::ostream& operator<<(std::ostream& os, const BigInt& bigInt)
{
os << bigInt.dec_rep;
return os;
}
int main()
{
using namespace std::string_literals;
BigInt n(1); //Line 9 where 1st error comes
BigInt n_default;
BigInt str_n("1"s); //Line 11 where 2nd error comes
std::cout << n << " " << n_default << " " << str_n << std::endl;
std::cout << "Hello world!" << std::endl;
return 0;
}
Last edited by 2kaud; July 17th, 2017 at 01:36 PM.
Reason: Added constr for char *
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.