C++: вложенный класс шаблонного класса

Рассмотрим следующий код:

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;
}

Так что здесь gcc-4.1.2 требует аргумент шаблона f быть явно указано. Это соответствует стандарту? Исправлена ​​ли эта проблема в более новых версиях GCC? Как я могу избежать явного указания int во время звонка f?

Обновление: вот обходной путь.

#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;
}

Тем не менее, я до сих пор не понимаю, зачем звонить f( x ); является недействительным. Можете ли вы сослаться на какой-то пункт в стандарте, который говорит, что такой вызов должен быть недействительным? Можете привести пример, когда такой вызов неоднозначен?

4 ответа

typename A<T>::B

Вот, T находится в не выводимом контексте, что означает, что T не может быть выведено из аргумента функции.

Проблема в том, что в общем случае существует потенциально бесконечное число возможных типов T это может соответствовать. Рассмотрим, например, если вместо struct B { }; ты имел typedef int B;,

Как я могу избежать явного указания int при вызове f?

Просто сделай B объявить свой тип класса вложенности

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

Тогда вы можете вывести это. Следующее принимает внешний шаблон, typedef и возвращаемый тип

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 */ 
}

В продолжение вопроса в "Обновлении", вот ситуация, в которой вызов f было бы неоднозначно (если бы это было разрешено, то есть):

// 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"
}

Обратите внимание на неоднозначную пару потенциальных экземпляров функций f: И то и другое f<int>() а также f<double> приведет к успешному вызову f(),

Как я могу избежать явного указания int при вызове f?

Вам понадобится небольшая помощь от 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

Называя это со следующим:

f(x, mytypeof(x.getType()));

В качестве альтернативы, вы можете абстрагироваться mytypeof(x.getType()) прочь, введя другую функцию, которая вызывает f, так что вы можете иметь свой оригинальный f(x), например

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));
}

Вы могли бы тогда позвонить f(x),

Другие вопросы по тегам