2011-08-09 9 views
36

Considere el siguiente extracto de la safe bool idiom:¿Hay casos donde un typedef es absolutamente necesario?

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

¿Es posible declarar la función de conversión sin el typedef? Lo siguiente no se compila:

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

¿por qué se declarará la función sin el typedef? –

+0

¿Es absolutamente necesaria la conversión a bool seguro? –

+0

@Tad: parece útil en mi caso particular (una plantilla de clase 'opcional '). – fredoverflow

Respuesta

9

Ah, acabo de recordar la identity meta-función. Es posible escribir

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

con la siguiente definición de identity:

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

Se podría argumentar que identity todavía utiliza un typedef, pero esta solución es "buena" suficiente para mí.

+0

Iba a responder bien esto ... 'identity' es una buena forma de evitar problemas de análisis. –

+0

@David: Lamentablemente,' identity' no es parte del estándar C++ 0x. En este caso, podríamos usar 'std :: decay', aunque ... – fredoverflow

+0

¡Esa es una mejor versión de lo que estaba buscando! – AJG85

2

Mi análisis dice que no es posible sin el uso de typedef. El compilador ve ( como el primer token y supone que está sobrecargando () operator, que no debería tener ningún argumento (los argumentos vendrían en el siguiente conjunto de paréntesis). Poner cualquier par de paréntesis adicionales tampoco ayudaría, pero en realidad confundiría al compilador y, por lo tanto, provocaría más errores.

La mayor parte del código STL está en la parte superior de typedef initions, ¡y debemos/debemos usarlos!

1

A typedef no es una macro, su segundo ejemplo no es equivalente al primero. En el primer caso, su typedef está definiendo un functor y luego usando ese tipo en un operador de conversión del tipo de functor. En el segundo, el operador está utilizando una sintaxis mala ya que no se ha especificado ningún operador porque no hay ningún tipo. No estoy seguro de cómo escribirlo, pero por lo general hay una manera.

Los typedefs no son realmente necesarios, excepto para hacer código legible para humanos en TMP e incluso eso depende del tipo de humano que sea.

Como no puedo encontrar la sintaxis alternativa, tal vez los typedefs sean necesarios en algunos casos. Solo pensé en otro posiblemente. Supongamos que tenía una plantilla con especializaciones que contenía un método estático con un tipo de retorno, como a continuación:

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

creo que en este caso también se podría necesitar el typedef con el fin de llamar al método estático independientemente de su especialización como de lo contrario el método podría confundir al compilador porque los tipos de devolución diferirían pero no sería una sobrecarga adecuada.

template <typename T> 
struct WhateverUser 
{ 
    typename WhateverHandler<T>::rType DoWhatever() 
    { 
     return WhateverHandler<T>::template Whatever(); 
    } 
}; 
3

Uno de los casos (no relacionada con su pregunta), donde se requiere un typedef es cuando se utiliza el

va_arg() macro. Citando el estándar C99 (7.15.1.1):

tipo * va_arg (va_list ap, tipo);

...

El parámetro tipo será un nombre tipo especificado de tal manera que el tipo de un puntero a un objeto que ha puede obtenerse del tipo especificado simplemente por postfixing una * a tipo

+1

hay otro caso como el llamado pseudo-destructor –

+0

Es decir 'p-> ~ unsigned char()' no está permitido (para aquellos que no saben qué es un pseudo-destructor o por qué necesitarías a typedef allí). – MSalters

3

contestar el "¿hay casos en que sea absolutamente necesario un typedef?"Del título de la pregunta, aquí es un ejemplo de dónde se necesita un typedef:

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

Ver los resultados aquí:. http://ideone.com/JPUra

+0

Compiler war again! GCC falla, VC tiene éxito. – Ajay

+1

¿Qué tal 'f (identity :: type())'?;) – fredoverflow

+2

@FredOverflow que parece que funcionará, aunque técnicamente 'type' es un typedef. : P –

2

Parece que la gramática exige el uso de un typedef en su caso, la conversión A -función-id debe ser de la forma operadorconversión de tipo-id. el tipo de conversión-id no puede contener paréntesis. Por lo que debe utilizar typedef al convertir a un tipo puntero a función o a un tipo de función de puntero a miembro .

+0

"La conversión-tipo-id no puede contener paréntesis". ¿De dónde sacas eso? – TonyK

+0

@TonyK: de la gramática. Básicamente es un tipo primitivo const/volátil o un id calificado (type-specifier-seq), seguido de cero o más punteros-operadores (conversion-declarator-opt). –

+0

@n: Encontré una forma: 'plantilla clase C {}; clase S {operador C <(99)> *() {return 0; }}; ' – TonyK

2

En C++ 11, puede hacerlo de esta manera (gcc 4.5.2):

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

No estoy diciendo que es bastante ...

+0

Espera, acabo de probarlo en VC10 y no funciona :(C2833 – fredoverflow

+0

¿Has probado con' decltype (& Testable :: foo) 'donde' foo' es un método miembro con la firma apropiada? –

+0

@David: Sí, no funciona tampoco :(C2833 otra vez – fredoverflow

1

yo sólo encontré con este tema, con el sonido metálico ++:

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

y hay una plantilla STL C++ 11 que cubre la identidad <T> funcionalidad:

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