2

TL;DR

I was recieving an undefiend reference error message when trying to run a program that had templated functions inside of a non-templated class. There were multiple solutions suggested in the comments of this thread, and the one I chose is illustrated at the end of this post.

Refer to the section headed as "Problem statement" bellow for the error output from my code.

Edit1: December 8, 2017:

  • Changed my code samples to allow easy reproduction of the problem by just doing a simple copy/pasta to their own ide.
  • A solution for this problem has been provided in the comments by both Kevin and DevSolar on this post -- big thanks guys! It turns out that I needed to do one of the following:
    • Move the definition for my template functions from the .cpp file to the .h file.
    • Explicitly provide definitions for the possible data types that could conceivably be passed through the template class type... This sounds like it would be a lot of work. I'm busy prepping for finals right now, but when I get a moment I'll come back and post the changed code to demo the fix.

Edit2: December 19, 2017:

  • Seeing as the problem in the title of this post stemmed from improperly defining my templated functions in my non-tmplated class, I've decided to add a TL;DR at the front of the post for future reader convenience.
  • Added my chosen solution strategy at the bottom of the posting.
    • This should offer a clear demonstration of one means for using a templated function in a non-templated class.

Problem statement

using g++ to compile and link my class files is giving me the undefined reference complaint where driver.cpp calls mergesortImproved::beginSorting(std::vector<int> &data). Apparently it can't find the definition for my function in mergesortImproved.cpp .

$ g++ -o msI_out -std=c++17 *.cpp
/tmp/ccZ0tzev.o: In function `generateTimeData(quicksort&, mergesort&, mergesortImproved&, int const&, long double&, std::vector<int, std::allocator<int> >&)': 
driver.cpp:(.text+0xaac): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
collect2: error: ld returned 1 exit status

Side note, msI is how I've been abbreviating mergesortImproved. This is the required file name from the instructor, but seeing as it's a bit cumbersome I've taken to calling it msI.

I say this just in case msI pops up somewhere and I forget to correct the name back to mergesortImproved in this post.

The specific details of this situation

All files are in the same folder, and I've been sure to include all files in my attempts to compile and link.

I've also tried to compile on both a Windows 10 machine and on a Linux machine.

I've read many of the other similar questions, and it seems that 9 out of 10 times this problem comes from the programmer not compiling all of the files for the program like the should. So...

edit: almost forgot to include the g++ versions for the windows and linux machines I tried to use.

For the Windows 10 machine:

>gcc -v
"crazy alphabet-soup pops up, followed by:"
Thread model: posix
gcc version 7.2.0 (x86_64-posix-seh-rev1, Built by MinGW-W64 project)

For the Linux machine:

$gcc -v
"like windows, it starts with crazy alphabet-soup, followed by:"
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

Here are the different g++ calls I've tried:

Note on the console output lines shown below:

All error responses were originally very long single lines of output. I've taken the liberty breaking that output into multiple lines for the sake of easier readability.

ie., no having to search for where the undefined reference is, along with other visual cues in terms of repeating format.

Linux attempts

  • Linux_attempt-1:

    $ g++ -o msI_out -std=c++17 *.cpp
    In function `generateTimeData(quicksort&, mergesort&, mergesortImproved&, int const&, long double&, std::vector<int, std::allocator<int> >&)':
    driver.cpp:(.text+0xaac): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2: error: ld returned 1 exit status
    

    (Same error as I showed in my problem statement above)

  • Linux_attempt-2:

    $ g++ -o msI_out -std=c++17 driver.cpp quicksort.cpp  mergesortImproved.cpp mergesort.cpp WorkDir.cpp
    /tmp/ccQzfFiq.o: In function `generateTimeData(quicksort&, mergesort&, mergesortImproved&, int const&, long double&, std::vector<int, std::allocator<int> >&)':
    driver.cpp:(.text+0xaac): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2: error: ld returned 1 exit status
    

    (Same error as I showed in my problem statement above)

  • Linux_attempt-3:

    $ g++ -c -std=c++17 driver.cpp quicksort.cpp  mergesortImproved.cpp mergesort.cpp WorkDir.cpp
    $ g++ -o msI_out -std=c++17 driver.o quicksort.o mergesortImproved.o mergesort.o WorkDir.o
    driver.o: In function `generateTimeData(quicksort&, mergesort&, mergesortImproved&, int const&, long double&, std::vector<int, std::allocator<int> >&)':
    driver.cpp:(.text+0xaac): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2: error: ld returned 1 exit status
    

Windows 10 attempts

  • Windows_attempt-1:

    >g++ -o msI_out -std=c++17 *.cpp
    \cc1alJ9m.o:driver.cpp:(.text+0x94f): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2.exe: error: ld returned 1 exit status
    
  • Windows_attempt-2:

    >g++ -o msI_out -std=c++17 driver.cpp mergesort.cpp quicksort.cpp mergesortImproved.cpp WorkDir.cpp
    \ccGBGXAX.o:driver.cpp:(.text+0x94f): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2.exe: error: ld returned 1 exit status
    
  • Windows_attempt-3:

    >g++ -c -std=c++17 driver.cpp quicksort.cpp  mergesortImproved.cpp mergesort.cpp WorkDir.cpp
    >g++ -o msI_out -std=c++17 driver.o quicksort.o mergesortImproved.o mergesort.o WorkDir.o
    driver.o:driver.cpp:(.text+0x94f): undefined reference to `void mergesortImproved::beginSorting<int>(std::vector<int, std::allocator<int> >&)'
    collect2.exe: error: ld returned 1 exit status
    

A peek inside the files

  1. driver.cpp
  2. mergesortImproved.h
  3. mergesortImproved.cpp

driver.cpp

    // driver.cpp 

#include <iostream>
#include <vector>
#include "mergesortImproved.h"     

using namespace std;

/** int main( int argc, char *argv[] )
 *
 * main(arc, argv) will perform the task of initializing sorting classes, output files, and control variables.
 *
 *
 * @param argc      The number of arguments passed on the command line.
 *                  If argc == 1, then no additional arguments were passed, as the first argument is always the
 *                  name of the executable file.
 *
 * @param argv      An array of c_strings containing any command-line arguments beyond the initial executable's name.
 * @return          the error code should any be thrown, otherwise 0 when all goes well.
 */
int main( int argc, char *argv[] )
{   
 vector<int> sampleVector = {41,  8467,  6334,  6500,  9169,  5724,  1478,  9358,  6962, 4464,  5705,  8145,  3281,  6827};      
  mergesortImproved msI;
  msI.beginSorting(sampleVector);
  return 0;
}

mergesortImproved.h

#ifndef MSI_ASSIG4_MERGESORTIMPROVED_H
#define MSI_ASSIG4_MERGESORTIMPROVED_H

#include <iostream>
#include <vector>
#include <stack>
#include <queue>

using namespace std;
class mergesortImproved
{
public:

/** template<class Comparable> void beginSorting(vector<Comparable> &data)
    *
    * Handles preparations for iterative-in-place Mergesort. Namely, the math needed for locating and saving index pointers into
    * queues and stacks that will later be used to emulate recursively subdeviding the unsorted collection before the much
    * simpler task of recombining the imaginary subdivisions.
    *
    * @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
    *                      Although there are no assert restrictions in place to ensure you only use data types with natural
    *                      ordering, the problem should become quickly apparant if the programmer fails to respect this
    *                      requirement.
    *
    * @param data          The vector<Comparable> that is to be sorted
    */
    template<class Comparable>
    void beginSorting(vector<Comparable> &vector);

/** template <class Comparable> mergesortImproved(vector<Comparable> &data)
    *
    * @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
    *                      Although there are no assert restrictions in place to ensure you only use data types with natural
    *                      ordering, the problem should become quickly apparant if the programmer fails to respect this
    *                      requirement.
    *
    * @param data          The vector<Comparable> that is to be sorted
    */
    template <class Comparable>
    mergesortImproved(vector<Comparable> &data)
    {
        beginSorting(data);
    }

    mergesortImproved()=default;

private:
    // omitted
};
#endif //MSI_ASSIG4_MERGESORTIMPROVED_H

mergesortImproved.cpp

#include "mergesortImproved.h"


using namespace std;


/** template<class Comparable> void beginSorting(vector<Comparable> &data)
*
* Handles preparations for iterative-in-place Mergesort. Namely, the math needed for locating and saving index pointers into
* queues and stacks that will later be used to emulate recursively subdeviding the unsorted collection before the much
* simpler task of recombining the imaginary subdivisions.
*
* @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
*                      Although there are no assert restrictions in place to ensure you only use data types with natural
*                      ordering, the problem should become quickly apparant if the programmer fails to respect this
*                      requirement.
*
* @param data          The vector<Comparable> that is to be sorted
*/
template<class Comparable>
void mergesortImproved::beginSorting(vector<Comparable> &data)
{
  // dummy functionality here, just demonstrating the conditions causing the linker error
  cout << "Hey, it's running!!" << endl;
}

Example of the easiest solution

  1. driver.cpp
  2. mergesortImproved.h
  3. mergesortImproved.cpp

driver.cpp

(driver.cpp is completely unchanged from before, but I've shown it's code here for visual ease in seeing how things tie together.)

    // driver.cpp 

#include <iostream>
#include <vector>
#include "mergesortImproved.h"     

using namespace std;

/** int main( int argc, char *argv[] )
 *
 * main(arc, argv) will perform the task of initializing sorting classes, output files, and control variables.
 *
 *
 * @param argc      The number of arguments passed on the command line.
 *                  If argc == 1, then no additional arguments were passed, as the first argument is always the
 *                  name of the executable file.
 *
 * @param argv      An array of c_strings containing any command-line arguments beyond the initial executable's name.
 * @return          the error code should any be thrown, otherwise 0 when all goes well.
 */
int main( int argc, char *argv[] )
{   
 vector<int> sampleVector = {41,  8467,  6334,  6500,  9169,  5724,  1478,  9358,  6962, 4464,  5705,  8145,  3281,  6827};      
  mergesortImproved msI;
  msI.beginSorting(sampleVector);
  return 0;
}

mergesortImproved.h

#ifndef MSI_ASSIG4_MERGESORTIMPROVED_H
#define MSI_ASSIG4_MERGESORTIMPROVED_H

#include <iostream>
#include <vector>
#include <stack>
#include <queue>

class mergesortImproved
{

public:

/** template<class Comparable> void beginSorting(vector<Comparable> &data)
*
* Handles preparations for iterative-in-place Mergesort. Namely, the math needed for locating and saving index pointers into
* queues and stacks that will later be used to emulate recursively subdeviding the unsorted collection before the much
* simpler task of recombining the imaginary subdivisions.
*
* @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
*                      Although there are no assert restrictions in place to ensure you only use data types with natural
*                      ordering, the problem should become quickly apparant if the programmer fails to respect this
*                      requirement.
*
* @param data          The vector<Comparable> that is to be sorted
*/
template<class Comparable>
void mergesortImproved::beginSorting(std::vector<Comparable> &data)
{
  // dummy functionality here, just demonstrating the conditions causing the linker error
  cout << "Hey, it's running!!" << endl;
}    


/** template<class Comparable> void beginSorting(vector<Comparable> &data)
*
* Handles preparations for iterative-in-place Mergesort. Namely, the math needed for locating and saving index pointers into
* queues and stacks that will later be used to emulate recursively subdeviding the unsorted collection before the much
* simpler task of recombining the imaginary subdivisions.
*
* @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
*                      Although there are no assert restrictions in place to ensure you only use data types with natural
*                      ordering, the problem should become quickly apparant if the programmer fails to respect this
*                      requirement.
*
* @param data          The vector<Comparable> that is to be sorted
*/
template<class Comparable>
void beginSorting(vector<Comparable> &vector);

/** template <class Comparable> mergesortImproved(vector<Comparable> &data)
    *
    * @tparam Comparable   This template reference is meant for use with data types that possess natural ordering.
    *                      Although there are no assert restrictions in place to ensure you only use data types with natural
    *                      ordering, the problem should become quickly apparant if the programmer fails to respect this
    *                      requirement.
    *
    * @param data          The vector<Comparable> that is to be sorted
    */
    template <class Comparable>
    mergesortImproved(vector<Comparable> &data)
    {
        beginSorting(data);
    }

    mergesortImproved()=default;

private:
    // omitted
};
#endif //MSI_ASSIG4_MERGESORTIMPROVED_H

mergesortImproved.cpp

// because my original sample code had only the template function
// in it, this sample piece now appears empty. 

// The reality is, I've moved all templated functions into the
// header file, which means this .cpp file only contains the 
// non templated functions, which don't need to be shown for the 
// purpose of this example.
RPeters
  • 107
  • 7
  • This is because it's a function template, which means you need to have the definition available in the same translation unit. Looking for a suitable duplicate question... – Kevin Dec 07 '17 at 21:59
  • Hm. Are you aware that either the compiler needs to *see* the *definition* of a template in the same compilation unit as where it's used (i.e., requiring you to put the definition in a header file), or -- this is the alternative -- you need to *instantiate* your templates inside `mergesortImproved.cpp` for each type you want to use the template with? – DevSolar Dec 07 '17 at 21:59
  • @Kevin: Ninja'd, with the dupe at least. ;-) – DevSolar Dec 07 '17 at 22:00
  • @DevSolar I commented first but you found the duplicate first :) – Kevin Dec 07 '17 at 22:00
  • @RPeters: Anyway, welcome to Stackoverflow, and thank you for making a very nice effort to include all relevant information! There is only one way this question could be even better -- if the code included were *just* enough to actually still compile (after copy & paste) and still reproduce the problem. It wouldn't need to do anything reasonable, just still trigger the error. We call that a [mcve]. – DevSolar Dec 07 '17 at 22:02
  • @DevSolar Ah, yeah I am about to walk out the door right now, but when I get back I'll modify the included code to permit copy/paste compilation. As for the templates, I hadn't even considered that to be the cause of my issue! I'll read more on how to implement your suggestion later, thanks! – RPeters Dec 07 '17 at 22:10
  • @Kevin Like what I said to DevSolar, thank you for the speedy diagnosis and response to my problem! I'll read more about how to properly define the template a bit later and post a corrected code presentation to show what I did to fix it. – RPeters Dec 07 '17 at 22:12
  • Just put the definition of the functions into the header (`mergesortImproved.h`) and put away `mergesortImproved.cpp`. That should fix the immediate issue. You see, the template is just that - a *template* to create a function for type X. When the compiler is asked (in `driver.cpp`) to instance the template for type X = `int`, it needs the *code* for the template -- which it doesn't have, it's in mergesortImproved.cpp, so the compiler just adds some reference. And when the compiler is compiling `mergesortImproved.cpp`, it doesn't get told to instance the template for `int`. (ctd.) – DevSolar Dec 07 '17 at 22:18
  • So the template never gets instanced for `int`, and the linker complains. -- Either put the code for all template functions / classes in the header (so the compiler has them at hand when compiling `driver.cpp`), or explicitly tell it to instance the template for `int` in `mergesortImproved.cpp`. The latter makes for somewhat "cleaner" code layout, but of course you don't get to use your template for any type not explicitly "forseen" by `mergesortImproved.cpp`, which is usually not what you want. – DevSolar Dec 07 '17 at 22:20
  • @DevSolar Well, that was a waaayy more intuitive to do than i had expected. Is there some way for me to call out your explanatory comments for kudos or something? Clear language descriptions like these are hard to come by it seems. – RPeters Dec 07 '17 at 23:28

0 Answers0