Consider the following code:
template < typename T >
struct A
{
struct B { };
};
template < typename T >
void f( typename A::B ) {
How can I avoid explicitly specifying int while calling f?
Just make B
declare its nesting class type
template < typename T >
struct A
{
struct B { typedef A outer; };
};
Then you can deduce it. The following takes the outer template, the inner's typedef and a return type
template<template<typename> class Outer, typename D, typename R = void >
struct nesting { };
template<template<typename> class Outer, typename Arg, typename R>
struct nesting< Outer, Outer<Arg>, R > {
typedef Arg arg1_type;
typedef R type;
};
template < typename T >
typename nesting<A, typename T::outer>::type
f(T) {
/* nesting<A, typename T::outer>::arg1_type is A's T */
}
How can I avoid explicitly specifying int while calling f?
You'll need a little help from struct B
.
template < typename T >
struct A
{
struct B
{
static T getType(); // no impl required
};
};
#define mytypeof(T) (true?0:T)
template < typename T, typename U >
void f( T t, U ) { } // U will be T of A<T>::B
Calling it with the following:
f(x, mytypeof(x.getType()));
Alternatively, you could abstract mytypeof(x.getType())
away by introducing another function which f calls, so you could have your original f(x)
. e.g.
template < typename T, typename U >
void b( T t, U ) { } // U will be T of A<T>::B
template < typename T >
void f( T t )
{
b(t, mytypeof(t));
}
You could then call f(x)
.
typename A<T>::B
Here, T
is in a nondeduced context, which means that T
cannot be deduced from the function argument.
The problem is that in the general case, there is a potentially infinite number of possible types T
that could match. Consider, for example, if instead of struct B { };
you had typedef int B;
.
Following up on the question in the "Update", here is a situation in which the call to f
would be ambiguous (if it were allowed, that is):
// Definitions of generic "struct A", as well as "f()", are the same as above
// But additionally, consider a specialized "struct A", defined as follows:
template <>
struct A<double>
{
typedef A<int>::B B;
}
// Now consider the call to "f", similarly to before:
int main()
{
// Possibility 1 for argument to "f()"
// A<int>::B x;
// Possibility 2 for argument to "f()": Use the specialized version of "struct A"
A<double>::B x;
f(x); // which value to deduce for type T? Could be "int" or "double"
}
Notice the ambiguous pair of potential instantiated functions f
: Both f<int>()
and f<double>
would result in a successfull call to f()
.