С++: вложенный класс класса шаблона
Рассмотрим следующий код:
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
?
Update:
Вот обходной путь.
#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 );
недействителен. Можете ли вы ссылаться на какой-то момент в стандарте, который говорит, что такой вызов должен быть недействительным? Можете ли вы привести пример, когда такой вызов неоднозначен?
Ответы
Ответ 1
typename A<T>::B
Здесь T
находится в неразрешенном контексте, что означает, что T
не может быть выведено из аргумента функции.
Проблема в том, что в общем случае существует потенциально бесконечное количество возможных типов T
, которые могли бы совпадать. Рассмотрим, например, если вместо struct B { };
вы имели typedef int B;
.
Ответ 2
Как я могу избежать явного указания 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 T */
}
Ответ 3
Как я могу избежать явного указания 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)
.
Ответ 4
В связи с вопросом в "Обновить" здесь возникает ситуация, при которой вызов 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()
.