0

I try to write a simple function my_to_string() in C++ that takes two iterators, first and last. I want the function to have a typical behavior: It shall iterate over [first,last) and do something with the objects' data with only read access to them. I have the declaration for this function in one file stringformat.h, its definition in the stringformat.cpp, and I try to call it from another .cpp file.

All files compile fine.

I use Eclipse CDT to build my project.

Now my problem is that I get the following linker error (straight copy-paste from Eclipse console, only home dir renamed for privacy reasons):

Invoking: Cross G++ Linker
g++  -o "agos2"  ./agos/pairingfunctions/CantorPairingFunction.o ./agos/pairingfunctions/CantorTupleIncrementer.o ./agos/pairingfunctions/PairingFunction.o  ./agos/machine/Instruction.o ./agos/machine/Machine.o ./agos/machine/MachineFactory.o ./agos/machine/Operation.o ./agos/machine/Program.o  ./agos/goedelnumbering/multiplier/InstructionGoedel.o ./agos/goedelnumbering/multiplier/InstructionIncrementer.o ./agos/goedelnumbering/multiplier/MachineIncrementer.o ./agos/goedelnumbering/multiplier/OperationIncrementer.o ./agos/goedelnumbering/multiplier/ProgramIncrementer.o  ./agos/BigIntegerLibrary/BigInteger.o ./agos/BigIntegerLibrary/BigIntegerAlgorithms.o ./agos/BigIntegerLibrary/BigIntegerUtils.o ./agos/BigIntegerLibrary/BigUnsigned.o ./agos/BigIntegerLibrary/BigUnsignedInABase.o  ./agos/Agos.o ./agos/Int64ToInt64Function.o ./agos/stringformat.o  ./agos.o   
./agos/machine/Machine.o: In function `agos::Machine::toStringWithState() const':
/home/username/workspace-cpp/agos2/Debug/../agos/machine/Machine.cpp:280: undefined reference to `std::string agos::my_to_string<__gnu_cxx::__normal_iterator<long const*, std::vector<long, std::allocator<long> > > >(__gnu_cxx::__normal_iterator<long const*, std::vector<long, std::allocator<long> > >, __gnu_cxx::__normal_iterator<long const*, std::vector<long, std::allocator<long> > >)'
collect2: error: ld returned 1 exit status
make: *** [agos2] Error 1

Code here:

stringformat.h

//...
template<class InputIterator>
std::string my_to_string(InputIterator first, InputIterator last);
//...

stringformat.cpp

//...
template<class InputIterator>
string my_to_string(InputIterator first, InputIterator last) {
    ostringstream oss;

    if (last - first > 0) {
        for (; first + 1 != last; ++first) {
            oss << *first << ' ';
        }

        oss << *first;
    }

    return oss.str();
}
//...

code using the function:

#include "../stringformat.h"
//...

void my_use_case() {
    vector<int64_t> const& v = ...;
    string s;
    s = my_to_string(v.begin(), v.end()); // Linker complains here; this is line 280
    //...
}

In the linker command, the stringformat.o is included, so I think the function should get found. I've also positively checked that a stringformat.o file exists in my project folder.

I've also tried copy-pasting the function definition right before my_use_case(). Then it compiles fine. No errors whatsoever. But this is not a nice solution.

Daniel S.
  • 6,458
  • 4
  • 35
  • 78
  • 1
    Template functions must be header-only – Simon Aug 05 '15 at 14:58
  • Come on, this is at least the fourth question about templates in cpp files today... – Quentin Aug 05 '15 at 15:01
  • uhhh...i've wasted hours. THANKS! – Daniel S. Aug 05 '15 at 15:01
  • @Quentin sure yes, the solution is super simple, but if you don't know its name, then it's hidden in google behind all the questions where people lack to link with the object file or where they are missing const, etc. in the function definition. – Daniel S. Aug 05 '15 at 15:05
  • @DanielS. true, and this is why duplicate questions are not deleted but kept as "search landing pads". But what surprises me is that a comparatively big number of people seem to use learning resources that half-teach what they're about. I mean, how have you come to know how to write a template, but haven't been told that it *must* be header-only ? – Quentin Aug 05 '15 at 15:10
  • 1
    There is *no* requirement that templates must be implemented in the header. That is simply the most convenient (usually) solution. (But I guess I'm being pedantic - for beginners it's simplest to just use the header always) – Aaron McDaid Aug 05 '15 at 15:13
  • @AaronMcDaid defined in such a way that the full definition is accessible at the point of instantiation ? Yep, I'll stick to "header-only" -- those for whom this rule does not fit will seek and find the complete one anyway :p – Quentin Aug 05 '15 at 15:18
  • @Quentin true. I've learned it by looking at code and not reading about theory. It didn't reach my awareness that the template implementations I was looking at were only in headers. – Daniel S. Aug 05 '15 at 15:22
  • It is possible for a template implementation to be available in only one translation unit and for another translation unit to call the implementation in the former translation unit. This works if the former translation unit explicitly instantiates each of the required templates. This also requires that the developer know in advance the full set of allowed template parameters - but in some contexts (not the context of this question, probably) this 'limitation' is a feature not a bug – Aaron McDaid Aug 05 '15 at 15:29
  • 1
    @DanielS. Oh, alright ! A word of advice then : C++ has its good share of constructs that only *appear* to work, or have weird reasons not to, so always cross-check what you learn. Best of luck with this wonderful language :) – Quentin Aug 05 '15 at 15:29
  • 1
    @AaronMcDaid I know, but this feature just moves the point of instantiation around, and full access to the definition is still needed there. – Quentin Aug 05 '15 at 15:31

0 Answers0