C ++: вложенный класс шаблонного класса - PullRequest
13 голосов
/ 04 ноября 2010

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

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 ]

10 голосов
/ 04 ноября 2010
typename A<T>::B

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

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

4 голосов
/ 05 ноября 2010

Как я могу избежать явного указания 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 */ 
}
0 голосов
/ 30 декабря 2013

В ответ на вопрос в «Обновлении», вот ситуация, в которой вызов 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().

0 голосов
/ 04 ноября 2010

Как мне избежать явного указания 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).

...