Анонимная структура в typedef класса признаков

Извините за забавный заголовок.

До С++ 0x существуют ограничения в использовании функций-локальных структур ( "локальные типы" ) в качестве аргументов шаблона. Мой вопрос по существу, если подобные ограничения применяются к анонимным структурам. В частности, в контексте класса признаков:

template <typename T>
struct trait;

template <>
struct trait<int> {
    typedef int type;
};

template <typename T>
struct trait<std::basic_string<T> > {
    typedef struct {
        T value;
    } type;
};

trait<std::string>::type foo; // Is this valid?

template <typename T>
void f() { }

f<trait<std::string>::type> >(); // Is this?

template <typename T>
void g() { f<typename trait<T>::type>(); }

g<std::string>(); // And this?

Является ли это действительным и надежным? Он компилируется в последних версиях GCC и LLVM, но Im по-прежнему не уверен, является ли это строго обоснованным и понимается ли его VС++ и ICC.

Ответы

Ответ 1

Объявление typedef, определяющее анонимный класс и имя typedef для этого класса, typedef-name - это имя класса для целей привязки. Поэтому законно использовать этот класс в качестве параметра шаблона, если он соответствует другим критериям.

См. 7.1.3p5 стандарта С++ 03

Если декларация typedef определяет неназванный класс (или перечисление), первый typedef-имя, объявленное decla-ration, является типом класса (или перечислением type) используется для обозначения типа класса (или тип перечисления) для целей привязки только (3.5). [Пример:

typedef struct { } *ps, S; // S is the class name for linkage purposes

Это 7.1.3p9 в С++ 0x FDIS.

FWIW, этот код компилирует ОК с MSVC2010 (по модулю опечаток).

Ответ 2

Для справки, цитата из связанного вопроса в 14.3.1/2:

Локальный тип, тип без привязки, неназванный тип или тип, смешанный от любого из этих типов не должно быть используется как аргумент шаблона для параметр типа шаблона.

Моя интерпретация заключается в том, что typedef struct создает псевдоним для неназванного типа и что он поэтому не может использоваться в качестве параметра типа шаблона. Далее отметим, что дополнительно в C typedef struct {} Foo; трактуется довольно иначе, чем struct Foo {};, давая прецедент, что две формы не являются эквивалентными (хотя, по общему признанию, разница не появляется в С++).

Таким образом, будет выглядеть ваш первый пример (поскольку он не использует неименованный тип в качестве параметра типа шаблона), тогда как второй и третий примеры будут технически недействительными (поскольку они используют его как параметр типа шаблона).

Наконец, в заключение я должен спросить, есть ли причина, по которой вы не можете назвать struct вместо typedef ing?

ИЗМЕНИТЬ: Из 7.1.3/1:

... Таким образом, typedef-name является синонимом другой тип. Имя typedef не ввести новый тип, как класс декларация (9.1) или перечисление делает...

Это сильно означает, что использование typedef таким образом не вводит тип, подходящий для использования в качестве параметра типа шаблона.

Ответ 3

В следующем стандарте это ограничение удаляется с языка. Стандарт говорит в

14.3.1 [temp.arg.type]/1

Аргумент шаблона для параметра шаблона, который является типом, должен быть идентификатором типа.

И typedef является допустимым идентификатором типа. На самом деле следующий параграф содержит такой пример:

14.3.1 [temp.arg.type]/2

template <class T> class X { };
template <class T> void f(T t) { }
void f() { 
   typedef struct { } B;
   B b;
   X<B> x3;
   f(b);
}

(Где я обрезал большинство других примеров). Пример показывает, что неназванный тип может использоваться как аргумент шаблона класса как в шаблонах классов, так и в шаблонах функций.

Ответ 4

Ну, это эквивалентно

template <typename T>
struct trait<std::basic_string<T> > {
    struct type {
        T value;
    };
};

который является полностью законным.