0

I have the following code structure (Resource and Parameter are empty classes):

Solver.cpp

#include "Solver.h"
#include "ValueFunction.h"

using namespace std;

template<typename T>
Solver<T>::Solver(vector<vector<Resource> >& resources, const Parameter& params) : 
    states(resources.size()) {
    for (int i=0; i<resources.size(); i++) {
        states[i] = State<T>(resources[i], params);
    }
}

// Explicit class declaration
template class Solver<ValueFunction>;

Solver.h

#ifndef SOLVER_H_
#define SOLVER_H_

#include <vector>
#include "Resource.h"
#include "Parameter.h"
#include "State.h"

template<typename T>
class Solver {
    public:
        Solver(
            std::vector<std::vector<Resource> >& resources,
            const Parameter& params
        );
    private:
        std::vector<State<T> > states;
};

#endif /* SOLVER_H_ */

State.cpp

#include "State.h"
#include "ValueFunction.h"

using namespace std;

template<typename T>
State<T>::State(vector<Resource>& _resources, const Parameter& params) : 
resources(_resources), valfuncs(_resources.size(), T(params)) {
}

template class State<ValueFunction>;

State.h

#ifndef STATE_H_
#define STATE_H_

#include <vector>
#include "Parameter.h"
#include "Resource.h"

template<typename T>
class State {
public:
    State() {};
    State(std::vector<Resource>& _resources, const Parameter& params);
    ~State() {};
private:
    std::vector<Resource> resources;
    std::vector<T> valfuncs;
};

#endif /* STATE_H_ */

ValueFunction.cpp

#include "ValueFunction.h"

ValueFunction::ValueFunction(const Parameter& _params) : params(_params) {
}

ValueFunction.h

#ifndef VALUEFUNCTION_H_
#define VALUEFUNCTION_H_

#include "Parameter.h"

class ValueFunction {
public:
    ValueFunction(const Parameter& _params);
private:
    const Parameter& params;
};

#endif /* VALUEFUNCTION_H_ */

With the following call:

#include "Solver.h"
#include "State.h"
#include "ValueFunction.h"
#include "Parameter.h"

using namespace std;

int main(int argc, char *argv[]) {
Parameter params;
    vector<vector<Resource> > resources(4);
    Solver<ValueFunction> sol(resources, params);
    return 0;
}

And I get the following error:

Solver.cpp:18:16:   instantiated from here
ValueFunction.h:6:21: error: non-static reference member ‘const Parameter& ValueFunction::params’, can't use default assignment operator

How can I invoke the non-default constructor for ValueFunction correctly, or is there an other way to initialize an std::vector with a non-default constructor (passing a constant reference)?

Update

The error is explained in this post. But the workaround for my problem is not entirly clear. Any suggestions?

Community
  • 1
  • 1
Reza
  • 360
  • 3
  • 17
  • The error occurs because I want to initialize `params` with `_params` in the class `ValueFunction` (No idea why this causes an error). – Reza Jun 04 '12 at 08:59
  • 2
    You'll have to put the definitions of the template class in the header files. Template definitions need to be visible at the point of instantiation. In your case they're not, so your compiler is confused. See [this question](http://stackoverflow.com/questions/3749099/why-should-the-implementation-and-the-declaration-of-a-template-class-be-in-the). I threw your code together in one source file and it compiles. – jrok Jun 04 '12 at 09:33
  • @jrok it is not a instatiation problem, as far as I know the implicit instantiation is as accepted by the compiler as the explicite (see [here](http://www.cplusplus.com/forum/articles/14272/)). – Reza Jun 04 '12 at 10:34
  • My code is compiling fine when I add the changes user315052 suggested. There's no need to put them all together in one file? Did you read the article about implicite instantiation? – Reza Jun 04 '12 at 11:11

1 Answers1

4

You are initializing the states of Solver using the form of the constructor that calls the default constructor of the vector members. You can pass in a second parameter to the vector constructor for states of type State<T>, and the vector will use the copy constructor to initialize the vector elements using that parameter as the source. Your loop in the Solver constructor will still work to provide the values you actually want within the states vector.

A reference cannot be initialized by the compiler generated default constructor. This is because it is the job of the default constructor to initialize the object, but a reference needs something to refer to. Just as you get an error declaring a reference variable with no initializer, the default constructor encounters the same problem.

int &r;      // this is an error
int i;
int &rr = i; // this is proper

Using the copy constructor version of the vector constructor helps you to avoid the problem, because the copy constructor initializes the reference with the value of the object it is copying. In this form of initializing a vector, each of the elements in states gets set to the same value as the first element.

...
    : states(resources.size(), State<T>(resources[0], params))
...

Perhaps the better way to go is to use the default constructor for the vector itself, and use reserve and push_back to add elements to the vector. This is better because it avoids creating any State<T> objects at all until it is actually added to the vector.

...
    : states()
...
    states.reserve(resources.size());
    for (int i...) {
        states.push_back(State<T>(resources[i], params));
    }
...

A third approach that would sort of allow your original code to work the way it was written is to define your own default constructor for ValueFunction that initializes the params member to something (perhaps the dreaded global).

class ValueFunction {
public:
    ValueFunction (); // define our own default constructor
    ...
};

const Parameter global_default_parameter;

ValueFunction::ValueFunction () : params(default_parameter) {}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • is there a way to pass the whole vector of resources to the constructor? As for example the first `State` of the vector `states` should be initialized with `resources[0]`, the second with `resources[1]` and so on? – Reza Jun 04 '12 at 11:04
  • @Reza, it would be wasted effort, because you would have to write a helper function to create the whole vector and then invoke the vector's copy constructor on `states` which would cause the vector to be created again. – jxh Jun 04 '12 at 15:07
  • your answer helped to solve one part of my problem (using the `State` constructor properly) but it fails to deliver a solution to the problem of the `std::vector` invoking the copy-constructor on a constant member. Thus your second approach is not working on my problem. My workaround was though something similar, you suggested in the last part, but not to use a `const Parameter&` anymore. – Reza Jun 05 '12 at 05:52
  • @Reza, I am not sure why the second approach would not work for you based on how you described your problem in your question, could you elaborate? – jxh Jun 07 '12 at 18:00