C++: nested class of a template class
Asked Answered
O

4

16

Consider the following code:

template < typename T >
struct A
{
    struct B { };
};

template < typename T >
void f( typename A<T>::B ) { }

int main()
{
    A<int>::B x;
    f( x );         // fails for gcc-4.1.2
    f<int>( x );    // passes
    return 0;
}

So here gcc-4.1.2 requires the template argument of f to be explicitly specified. Is this meet the standard? Does the newer versions of GCC have this issue fixed? How can I avoid explicitly specifying int while calling f?

Update: Here is a workaround.

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>

template < typename T >
struct A
{
    typedef T argument;
    struct B { typedef A outer; };
};

template < typename T >
void f( typename A<T>::B ) { }

template < typename Nested >
void g( Nested )
{   
    typedef typename Nested::outer::argument TT;
    BOOST_STATIC_ASSERT( (boost::is_same< typename A<TT>::B, Nested >::value) );
}

struct NN 
{
    typedef NN outer;
    typedef NN argument;
};

int main()
{
    A<int>::B x;
    NN y;
    g( x );  // Passes
    g( y );  // Fails as it should, note that this will pass if we remove the type check
    f( x );  // Fails as before

    return 0;
}

However, I still can't see why call f( x ); is invalid. Can you refer to some point in the standard which says such call should be invalid? Can you bring an example where such call is ambiguous?

Oscillation answered 3/11, 2010 at 22:29 Comment(0)
L
13
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;.

Longe answered 3/11, 2010 at 22:30 Comment(1)
Thanks for your answer. Why T cannot be deduced from the function argument? Can you bring an example where there are two types for T which match for one particular call of f? Do you mean, that for another specialization of A it could be another typedef int B; instead of struct B {};? I can't see why in this case the call of f should be ambiguous.Oscillation
H
4

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 */ 
}
Habited answered 5/11, 2010 at 13:31 Comment(0)
H
0

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

Highpressure answered 3/11, 2010 at 23:24 Comment(1)
Thanks for your answer. Adding an extra parameter will not work for me, because actually I have overladed operators instead of f.Oscillation
T
0

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

Truthful answered 30/12, 2013 at 15:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.