zku11126
zku11126

Reputation: 103

How to use an object instance as template argument?

I'm learning C++. I have a Classroom class which should behave one way or another depending on the Configuration object that is being used. I could pass that Configuration object in the constructor when creating the Classroom object like this:

class Classroom {
private:
    Configuration conf;
public:
    Classroom(Configuration conf_){
        conf = conf_;
    }
    /** more member functions that use conf **/
};

But I thought it would be cooler if I could use a template for it. The Configuration object would be passed as template argument when creating the Classroom object. This is what I came up with, but it doesn't work:

template<Configuration &conf>
class Classroom {
    int doSomething(int n){
        // member function that uses data in Configuration object
        return n + conf.config_1;
    }
};

struct Configuration {
public:
    int config_1;
};

int main() {
    Configuration conf;
    conf.config_1 = 95;
    Classroom<conf> myClassroom;// doesn't work
}

It says: error: the value of 'conf' is not usable in a constant expression.

What am I missing?

Upvotes: 10

Views: 7005

Answers (2)

skypjack
skypjack

Reputation: 50550

You can do that with some limitations. The way you are trying to do it isn't valid. It's a matter of storage.

If you need more than one configuration around, you can define it as a static member of a class template or define a global array of Configurations:

struct Configuration {
    int config_1;
};

template<int>
struct Accessor {
    static Configuration configuration;
};

template<int N>
Configuration Accessor<N>::configuration;

template<Configuration &conf>
class Classroom {
    int doSomething(int n){
        return n + conf.config_1;
    }
};

int main() {
    Accessor<1>::configuration.config_1 = 95;
    Classroom<Accessor<1>::configuration> myClassroom;
    (void)myClassroom;
}

If you can stick with a single instance, you can even put it in the global scope and use it instead:

struct Configuration {
    int config_1;
};

Configuration conf;

template<Configuration &conf>
struct Classroom {
    int doSomething(int n){
        return n + conf.config_1;
    }
};

int main() {
    conf.config_1 = 95;
    Classroom<conf> myClassroom;
    myClassroom.doSomething(42);
}

Other solutions are possible, but I'm sure you got the idea.


See the examples up and running on wandbox.

Upvotes: 6

user0042
user0042

Reputation: 8018

Template parameters are meant to be types or at least a restricted set of literals or enum values which are known at compile time.

So you cannot do that.


What you can do though is something like

template<typename ConfType>
class Classroom {
    const ConfType& conf_;
public:
    // Provide a constructor that takes a reference to the Configuration type
    Classroom(const ConfType& conf) : conf_(conf) {}
    int doSomething(int n){
        // member function that uses data in Configuration object
        return n + conf.config_1;
    }
};

struct Configuration {
public:
    int config_1;
};

int main() {
    Configuration conf;
    conf.config_1 = 95;
    Classroom<Configuration> myClassroom(conf);
}

Upvotes: 0

Related Questions