CAdaker
CAdaker

Reputation: 14701

Class constructor with non-argument template type

For a normal C++ function, it's possible to have template parameters not appearing in the argument list:

template<typename T>
T default_construct()
{
    return T();
}

and call this with

some_type x = default_construct<some_type>();

Even though the type I'm using is not in the argument list, I can still pass it to the function. Now, I want to do this in a class constructor:

struct Base;

template<typename T>
Base* allocate()
{
    return new T; //Assume T derives from Base...
}

struct factory {
    template<typename T>
    factory()
        : func(allocate<T>)
    {}

    std::tr1::function<Base*()> func;
};

but I can't find a way to supply the parameter to the constructor when I want to construct an instance of factory.

Is there a way to do this without turning the class into a templated class or sending some unused T object to the constructor?

Upvotes: 3

Views: 2002

Answers (3)

j_random_hacker
j_random_hacker

Reputation: 51226

litb's and wrang-wrang's answers are good. As one more possibility, you might consider declaring all your (non-copy-) constructors private or protected and creating one or more static member function templates factory create<T>(). Then, to define a factory instance, instead of

factory<SomeType> f;                      // 1 (doesn't compile)

You would write

factory f(factory::create<SomeType>());   // 2

Clearly not as pretty as (1), but IMHO slightly clearer than using type tags. (The compiler will eliminate the copy in practice.)

BTW is there a reason why you could not simply make factory a class template? Then the syntax from (1) would compile. (It would mean however that factories of different types could not be assigned to one another.)

Upvotes: 0

Jonathan Graehl
Jonathan Graehl

Reputation: 9301

No, you can't. The use of an unused object of a given type is called using a "type tag object". You can either create globals of each type, or use the default constructor every time.

You can reasonably hope that if the constructor is inlined, the type tag will never actually be created.

Upvotes: 3

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506847

No, there is no way to do that. The note at 14.8.1/5 in the Standard explains why

[Note: because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. ]

Of course, it doesn't need to be a T object you send. It can be any object that has T encoded in its type

template<typename T> struct type2type { };

struct factory {
    template<typename T>
    factory(type2type<T>)
        : func(allocate<T>)
    {}

    std::tr1::function<Base*()> func;
};

factory f((type2type<Foo>()));

Upvotes: 10

Related Questions