tags:

views:

384

answers:

3

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?

+7  A: 

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>()));
Johannes Schaub - litb
Well, the quote from the standard answers that pretty clearly. Thanks.
CAdaker
+3  A: 

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.

wrang-wrang
+1  A: 

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.)

j_random_hacker