Jackson Ray Hamilton
Jackson Ray Hamilton

Reputation: 9466

C++ undefined reference to `Vector_container::Vector_container(std::initializer_list<double>)'

I'm trying to compile the code from A Tour of C++, Chapter 4.3, which describes "Abstract Types". However, I am getting a compiler error:

$ g++ -std=c++11 abstract.cpp Vector.cpp Vector_container.cpp
/tmp/ccToFsNS.o: In function `g()':
abstract.cpp:(.text+0x9c): undefined reference to `Vector_container::Vector_container(std::initializer_list<double>)'
abstract.cpp:(.text+0xb4): undefined reference to `Vector_container::~Vector_container()'
abstract.cpp:(.text+0xc5): undefined reference to `Vector_container::~Vector_container()'
collect2: error: ld returned 1 exit status

How can I fix my code so it compiles? I am brand new to C++ and am trying to transcribe Stroustrup's examples to the best of my ability.

abstract.cpp:

#include "Vector_container.hpp"
#include <iostream>

void use(Container& c)
{
  const int sz = c.size();
  for (auto i = 0; i != sz; ++i)
    {
      std::cout << c[i] << "\n";
    }
}

void g()
{
  Vector_container vc {10, 9, 8, 7, 6};
  use(vc);
}

int main()
{
  g();
}

Vector_container.hpp:

#include "Container.hpp"
#include "Vector.hpp"
#include <initializer_list>

#pragma once

class Vector_container : public Container {
  Vector v;
public:
  Vector_container(int);
  Vector_container(std::initializer_list<double>);
  ~Vector_container();
  double& operator[](int);
  int size() const;
};

Vector_container.cpp:

#include "Container.hpp"
#include "Vector.hpp"

class Vector_container : public Container {
  Vector v;
public:
  Vector_container(int s) : v(s) {}
  Vector_container(std::initializer_list<double> lst) : v{lst} {}
  ~Vector_container() {}
  double& operator[](int i) { return v[i]; }
  int size() const { return v.size(); }
};

container.hpp:

#pragma once

class Container {
public:
  virtual double& operator[](int) = 0;
  virtual int size() const = 0;
  virtual ~Container() {}
};

Vector.hpp:

#include <initializer_list>

#pragma once

class Vector {
public:
  Vector(int);
  Vector(std::initializer_list<double>);
  ~Vector();
  double& operator[](int);
  int size() const;
  void push_back(double);
private:
  double* elem;
  int sz;
};

Vector.cpp:

#include "Vector.hpp"
#include <stdexcept>

Vector::Vector(int s) : elem{new double[s]}, sz{s}
{
  if (s < 0)
    {
      throw std::length_error{"Size must be >= 0"};
    }
  for (auto i = 0; i != s; ++i)
    {
      elem[i] = 0;
    }
}

Vector::Vector(std::initializer_list<double> lst)
  : elem{new double[lst.size()]}, sz{static_cast<int>(lst.size())}
    {
      std::copy(lst.begin(), lst.end(), elem);
    }

Vector::~Vector() {
  delete[] elem;
}

double& Vector::operator[](int i)
{
  if (i < 0 || size() <= i)
    {
      throw std::out_of_range{"Vector::operator[]"};
    }
  return elem[i];
}

int Vector::size() const
{
  return sz;
}

Upvotes: 0

Views: 1227

Answers (2)

Jackson Ray Hamilton
Jackson Ray Hamilton

Reputation: 9466

As noted by commenters and Sam, I was not defining Vector_container correctly. I changed Vector_container.cpp to:

#include "Vector_container.hpp"

Vector_container::Vector_container(int s) : v(s) {}
Vector_container::Vector_container(std::initializer_list<double> lst) : v{lst} {}
Vector_container::~Vector_container() {}

double& Vector_container::operator[](int i)
{
  return v[i];
}
int Vector_container::size() const
{
  return v.size();
}

And now the program compiles and runs as expected.

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118352

I have not read that particular book, but I would be surprised to learn that Stroustrup would write something that:

1) Explicitly depends on gcc-specific pragmas, and

2) Encourage the abuse of #pragma once

3) Offers an example of this kind of bad practice of declaring a class in the header file, but then define the class's methods in a completely separate translation unit that does not even include the header file.

Just get rid of your Vector_container.hpp entirely, then rename Vector_container.cpp to Vector_container.hpp, and the code should compile and link just fine.

Your problem is that nothing in the existing Vector_container.cpp translation unit actually ends up instantiating and compiling the class methods, in order for the linker to find and resolve the references from your other translation units.

You could also replace the inline definitions in Vector_container.cpp with non-inline definitions. This will also instantiate them. But this won't do anything about the duplicate definition in Vector_container.hpp, which I think is very bad practice.

Upvotes: 0

Related Questions