Есть ли случаи, когда typedef абсолютно необходим?

Рассмотрим следующий отрывок из идиомы safe bool:

typedef void (Testable::*bool_type)() const;
operator bool_type() const;

Можно ли объявить функцию преобразования без typedef? Следующее не компилируется:

operator (void (Testable::*)() const)() const;

person fredoverflow    schedule 09.08.2011    source источник
comment
Зачем вам объявлять функцию без typedef?   -  person Cheers and hth. - Alf    schedule 09.08.2011
comment
Абсолютно ли необходимо преобразование в Safe Bool?   -  person Tadeusz Kopec    schedule 09.08.2011
comment
@Tad: Это кажется полезным в моем конкретном случае (шаблон класса optional<T>).   -  person fredoverflow    schedule 09.08.2011
comment
@Tad: Я сам довольно часто использую идиому safe bool, @Fred: Я бы заметил, что typedef помогает создавать более читаемый код / ​​ошибки.   -  person Matthieu M.    schedule 09.08.2011
comment
open-std.org/jtc1/sc22/wg21/ docs / cwg_active.html # 395   -  person Johannes Schaub - litb    schedule 09.08.2011
comment
Не относись ко мне так серьезно. Это была неудачная попытка получить значок эксперта :-)   -  person Tadeusz Kopec    schedule 10.08.2011
comment
@Johannes: Но template <typename T> using id = T; нет в стандартной библиотеке, верно? :(   -  person fredoverflow    schedule 10.08.2011


Ответы (8)


Ах, я только что вспомнил о мета-функции identity. Можно написать

operator typename identity<void (Testable::*)() const>::type() const;

со следующим определением identity:

template <typename T>
struct identity
{
    typedef T type;
};

Вы можете возразить, что identity по-прежнему использует typedef, но это решение для меня достаточно "хорошее".

person fredoverflow    schedule 09.08.2011
comment
Я собирался ответить правильно ... identity - хороший способ обойти проблемы синтаксического анализа. - person David Rodríguez - dribeas; 09.08.2011
comment
@David: К сожалению, identity не является частью стандартного C ++ 0x. В этом случае мы могли бы использовать std::decay, хотя ... - person fredoverflow; 09.08.2011
comment
Это лучшая версия того, что я хотел! - person AJG85; 21.09.2011

Один случай (не связанный с вашим вопросом), когда требуется typedef, - это использование

va_arg() макрос. Цитата из стандарта C99 (7.15.1.1):

тип * va_arg (va_list ap, тип);

...

Параметр type должен быть именем типа, указанным таким образом, чтобы тип указателя на объект, имеющий указанный тип, мог быть получен просто путем постфиксации * к type

person Keith Thompson    schedule 09.08.2011
comment
есть еще такой случай, а именно вызов псевдодеструктора - person Cheers and hth. - Alf; 09.08.2011
comment
Т.е. p->~unsigned char() не разрешено (для тех, кто не знает, что такое псевдодеструктор или зачем вам там typedef). - person MSalters; 10.08.2011

Отвечая на вопрос "Есть ли случаи, когда typedef абсолютно необходим?" из заголовка вопроса, вот один из примеров, когда требуется typedef:

f(unsigned char());   // compiler error!
typedef unsigned char Byte;
f(Byte());            // fine!

См. Результаты здесь: http://ideone.com/JPUra

person Community    schedule 09.08.2011
comment
Снова война компиляторов! GCC терпит неудачу, VC преуспевает. - person Ajay; 09.08.2011
comment
Как насчет f(identity<unsigned char>::type())? ;) - person fredoverflow; 09.08.2011
comment
@FredOverflow, похоже, будет работать, хотя технически type - это typedef. :П - person ; 09.08.2011

Мой анализ говорит, что это невозможно без использования typedef. Компилятор видит ( как первый токен и предполагает, что вы перегружаете () operator, у которого не должно быть никаких аргументов (аргументы будут указаны в следующем наборе круглых скобок). Добавление любых дополнительных скобок тоже не поможет, но на самом деле запутает компилятор и, следовательно, приведет к еще большему количеству ошибок.

Большая часть кода STL находится поверх typedefinitions, и мы должны / должны их использовать!

person Ajay    schedule 09.08.2011

Кажется, что грамматика в вашем случае требует использования typedef. идентификатор-функции-преобразования должен иметь вид оператор идентификатор-типа-преобразования. идентификатор-типа-конверсии не может содержать круглых скобок. Таким образом, вы должны использовать typedef при преобразовании в тип указателя на функцию или в тип указателя на функцию-член.

person n. 1.8e9-where's-my-share m.    schedule 09.08.2011
comment
Идентификатор типа преобразования не может содержать скобок. Откуда это у тебя? - person TonyK; 09.08.2011
comment
@TonyK: из грамматики. В основном это может быть примитивный тип const / volatile или квалифицированный идентификатор (type-Specifier-seq), за которым следует ноль или более операторов-указателей (convert-declarator-opt). - person n. 1.8e9-where's-my-share m.; 09.08.2011
comment
@n: Я нашел способ: template <int T> class C { } ; class S { operator C<(99)>*() { return 0 ; } } ; - person TonyK; 12.08.2011

В C ++ 11 это можно сделать так (gcc 4.5.2):

operator decltype((void (Testable::*)() const)(0))() const ;

Я не говорю, что это красиво ...

person TonyK    schedule 09.08.2011
comment
Вы пробовали использовать decltype( &Testable::foo ), где foo - метод-член с соответствующей подписью? - person David Rodríguez - dribeas; 09.08.2011

typedef не является макросом, ваш второй пример не эквивалентен первому. В первом случае ваш typedef определяет функтор, а затем использует этот тип в операторе приведения типа функтора. Во втором случае оператор использует неверный синтаксис, так как оператор не указан, потому что нет типа. Я не знаю, как это написать, но обычно способ есть.

Typedef на самом деле не нужен, за исключением создания удобочитаемого кода в TMP, и даже тогда это зависит от того, какой вы человек.

Поскольку я не могу придумать альтернативный синтаксис, возможно, в некоторых случаях необходимы typedef. Я просто подумал, возможно, о другом. Допустим, у вас есть шаблон со специализациями, который содержит статический метод с возвращаемым типом, как показано ниже:

template <typename T>
struct WhateverHandler
{
   typedef T rType;
   static rType Whatever() { return rType(); }
};

template <>
struct WhateverHandler<std::string>
{
   typedef std::string rType;
   static rType Whatever() { return rType(); }
};

Я думаю, что в этом случае вам также понадобится typedef для вызова статического метода независимо от специализации, поскольку в противном случае метод может запутать компилятор, потому что возвращаемые типы будут отличаться, но это не будет правильной перегрузкой.

template <typename T>
struct WhateverUser
{
   typename WhateverHandler<T>::rType DoWhatever()
   {
       return WhateverHandler<T>::template Whatever();
   }
};
person AJG85    schedule 09.08.2011

Я только что столкнулся с этой проблемой с clang ++:

foo.cpp:17:8: error: must use a typedef to declare a conversion to 'void (*(int))()'

и есть шаблон C ++ 11 STL, который охватывает функциональность идентичности ‹T›:

#include <type_traits>
…
struct foo {
     void bar( ) const { }
     operator std::common_type<void(foo::*)( )const>::type( ) { return &foo::bar; }
};
person jetson    schedule 07.03.2014