template class instantiate without template parameter, automatic type deduction

F

Fei Liu

Hello,
We all know that a template function can automatically deduce its
parameter type and instantiate, e.g.

template <tpyename T>
void func(T a);

func(0.f);

This will cause func<float> to be instantiated. The user does not have
to explicitly call func<float>(0.f);

However this line of thinking is broken when it comes to a template
class constructor function, e.g.

class A{
int x;
};

template <typename T>
class C{
C(const T & t) : t(t) {}
T t;
};

template <>
class C<A> {
C(const A & t) : t(t) {}
A t;
};


int main(){

A a;
C c(a);
}

The above code can't be successfully compiled. One has to name the
type returned from the constructor call to pick up the object. But
what really distinguishes from normal function call is that even C(a)
fails, compiler comlains missing template argument. The problem is
sometimes you want automatic (auto) type deduction that a compiler can
provide but you can't get it for constructor call.

What's the current best practice to approach such kind of problem,
i.e. automatic type deduction?

Fei
 
F

Fei Liu

Fei said:
Hello,
We all know that a template function can automatically deduce its
parameter type and instantiate, e.g.

I apologize for the double posting...Wierd seamonkey email/newsreader
client issue.
 
V

Victor Bazarov

Fei said:
Hello,
We all know that a template function can automatically deduce its
parameter type and instantiate, e.g.

template <tpyename T>
void func(T a);

func(0.f);

This will cause func<float> to be instantiated. The user does not have
to explicitly call func<float>(0.f);

However this line of thinking is broken when it comes to a template
class constructor function, e.g.

class A{
int x;
};

template <typename T>
class C{
C(const T & t) : t(t) {}
T t;
};

template <>
class C<A> {
C(const A & t) : t(t) {}
A t;
};


int main(){

A a;
C c(a);
}

The above code can't be successfully compiled. One has to name the
type returned from the constructor call to pick up the object. But
what really distinguishes from normal function call is that even C(a)
fails, compiler comlains missing template argument.

Correct. The syntax involves the _type_ name, not a function name.
The problem is
sometimes you want automatic (auto) type deduction that a compiler can
provide but you can't get it for constructor call.

That's not true at all. In your example it's not the constructor that
is a template for which the argument needs to be deduced, it's the type
template that needs to be instantiated. When constructors are concerned,
this is the correct (and working) example:

struct C {
template<class T> C(const T& t) {}
};

struct A {};

int main() {
A a;
C c(a); // compiles just fine
}
What's the current best practice to approach such kind of problem,
i.e. automatic type deduction?

Wrap it in a function template.

V
 
F

Fei Liu

Victor said:
Correct. The syntax involves the _type_ name, not a function name.


That's not true at all. In your example it's not the constructor that
is a template for which the argument needs to be deduced, it's the type
template that needs to be instantiated. When constructors are concerned,
this is the correct (and working) example:

struct C {
template<class T> C(const T& t) {}
};

Well you see this is not the complete code I presented intially. You
threw away class member t with your convenient modification. If it needs
to be:
template <typenaem U>
struct C{
template<class T> C(const T& t) : t(t) {}
U t;
};
We are back to step 1, once you have a class member whose type depends
on class template parameter.
struct A {};

int main() {
A a;
C c(a); // compiles just fine
}


Wrap it in a function template.
Indeed I played with it for a bit, but it does not seem to be that
great, for example I could conjure a function like following to simulate
a constructor:
template <typename T, template <typename > class RT>
RT<T> create(const T& t){
return RT<T>(t);
}

It quickly becomes obvious that RT<T> needs to be given to instantiate
it and one does not gain anything.

Care to supply an example to illustrate your point?
 
V

Victor Bazarov

Fei said:
Well you see this is not the complete code I presented intially. You
threw away class member t with your convenient modification.

It's not "my convenient modification". It's essential to prove the
point. A class template argument _cannot_ be deduced from anything
when trying to define an object of the class instantiated from the
class template. If it were possible, it would be catch-22. Imagine
you have

template<class T> struct C;
template<> struct C<int> {
C(char);
C(double);
};

template<> struct C<char> {
C(bool);
};

Now, how the hell should the compiler decide what 'T' should be when
you declare an object like so

C a(0);

?
If it
needs to be:
template <typenaem U>
struct C{
template<class T> C(const T& t) : t(t) {}
U t;
};
We are back to step 1, once you have a class member whose type depends
on class template parameter.

No, we're not back to step 1. You're confusing two concepts: defining
a type with which to declare an object and deduction of the function
template argument type from the argument the function is called with.

In this particular case you have two template arguments which are NOT
related at all. Making your compiler deduce the 'T' (the template
argument for the constructor) has NO EFFECT on the need to provide
the template argument for 'C' class template when declaring an object
with it -- you need a _type_, not a template, to declare an object.
Indeed I played with it for a bit, but it does not seem to be that
great, for example I could conjure a function like following to
simulate a constructor:
template <typename T, template <typename > class RT>
RT<T> create(const T& t){
return RT<T>(t);
}

It quickly becomes obvious that RT<T> needs to be given to instantiate
it and one does not gain anything.

Care to supply an example to illustrate your point?

Well, wrapping the in a function will not really help you declare or
define a stand-alone object. In order to do that you still need to
instantiate the template, which requires that you _spell it out_:

C<blah> ...

You can, however, use the function wrapper to provide the mechanism
for instantiating a _temporary_ of your type when using it as part of
an expression. Given

template<class T> class C {
T t;
public:
C(T const& t) : t(t) {}
};

template<class T> C<T> makeC(T const& t) {
return C<T>(t);
}

you *cannot* write

... C(42) ...

and expect C<int> to be instantiated. You still would have to write

... C<int>(42) ...

However, you *can* write

... makeC(42) ...

in which case a temporary of type C<int> _will_ be created (after
C<int> is instantiated). That's what I meant.

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top