Есть ли случаи, когда typedef абсолютно необходим? - PullRequest
36 голосов
/ 09 августа 2011

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

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

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

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

Ответы [ 8 ]

9 голосов
/ 09 августа 2011

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

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

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

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

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

3 голосов
/ 09 августа 2011

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

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

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

3 голосов
/ 09 августа 2011

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

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

type * va_arg (va_list ap, type );

...

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

2 голосов
/ 09 августа 2011

В C ++ 11 вы можете сделать это следующим образом (gcc 4.5.2):

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

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

2 голосов
/ 09 августа 2011

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

2 голосов
/ 09 августа 2011

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

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

1 голос
/ 08 марта 2014

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

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

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

#include <type_traits>
…
struct foo {
     void bar( ) const { }
     operator std::common_type<void(foo::*)( )const>::type( ) { return &foo::bar; }
};
1 голос
/ 09 августа 2011

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

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

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

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