The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

466476 (1) [Avatar] Offline
#1
Hi I got some problems to put the reverse_pairs function which mentioned at the end of chapter 4

here is my header file: lib.h

#include <vector>

template <
typename C,
  typename P1 = typename std::remove_cv<
  typename C::value_type::first_type>::type,
  typename P2 = typename C::value_type::second_type
  >
auto reverse_pairs(const C &items) -> std::vector<std::pair<P2, P1>>;


the related implementation file: lib.cc

#include <algorithm>
#include "lib.h"

template <
  typename C,
  typename P1 = typename std::remove_cv<
    typename C::value_type::first_type>::type,
  typename P2 = typename C::value_type::second_type
  >
auto reverse_pairs(const C &items) -> std::vector<std::pair<P2, P1>> {
  std::vector<std::pair<P2, P1>> result(items.size());

  std::transform(items.cbegin(),
                 items.cend(),
                 result.begin(),
                 [](const std::pair<const P1, P2> &p) {
                   return std::make_pair(p.second, p.first);
                 });

  return result;
}


the main file: main.cc

#include <iostream>
#include <algorithm>
#include "lib.h"

using namespace std;

int main() {
    std::vector<std::pair<int, int>> pairs = { {1, 2}, {2, 3}, {3, 4} };
    auto reversed = reverse_pairs(pairs);
    std::for_each(reversed.cbegin(),
                  reversed.cend(),
                  // oh, this const qualifier seems to be mandantory here
                  [](const std::pair<int, int> &p) -> void {
                    std::cout << p.first << "-" << p.second << " ";
                  });
    std::cout << std::endl;
}


But when I try to compile it using

g++-4.9 -std=c++14 main.cc lib.cc -o main && ./main


It complains that

/tmp/ccQ30s9i.o: In function `main':
main.cc:(.text+0xd2): undefined reference to `std::vector<std::pair<int, int>, std::allocator<std::pair<int, int> > > reverse_pairs<std::vector<std::pair<int, int>, std::allocator<std::pair<int, int> > >, int, int>(std::vector<std::pair<int, int>, std::allocator<std::pair<int, int> > > const&)'
collect2: error: ld returned 1 exit status


But if I change the template to a trivial type, then the compiler will be happy to compile that for me e.g.:

auto reverse_pairs(std::vector<std::pair<int, int>> items) -> std::vector<std::pair<int, int>> {
  std::vector<std::pair<int, int>> result(items.size());

  std::transform(items.cbegin(),
                 items.cend(),
                 result.begin(),
                 [](const std::pair<const int, int> &p) {
                   return std::make_pair(p.second, p.first);
                 });

  return result;
}


So how can I put that generic reverse_pairs function in a header file and then include it? Thanks in advance!

Ivan Cukic (104) [Avatar] Offline
#2
Hi,

When the template functions (and classes) are concerned, you should put the whole implementation into the header file - not just the declaration*.

Templates in C++ are unique - quite different to what you might have seen in other languages. They are just blueprints for making functions, they are not functions themselves.

A proper function gets created when you try to use it (to instantiate the template), and at that point the whole blueprint should be known.

For example, if you create a template function min<T,T>, but never use it, when you compile the program, not a single line of that function will end up in the compiled binary.

If you call it for min(1, 2), the binary will contain the implementation of min for ints - min<int,int>.

If you call it for min(1, 2) and min(1.0, 2.5), the compiled binary will contain the implementation for min<int, int> and a separate implementation for min<double, double>.


You can also check out the longer explanation at the official C++ FAQ -- https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl

Cheers,
Ivan

* a common alternative to this is to have the declarations in a .h file, and include the .cpp file from it (reversed to the normal situation of including .h from .cpp) - which effectively makes the whole implementation of the template function or a class available by includeing the .h file

p.s. You will probably need to update your compiler for the later examples - gcc 4.9 is quite old (the current stable version is 7.1).