2011-05-18 6 views
11

El bucle ranurado de C++ 0x tiene una excepción especial para manejar matrices (FDIS §6.5.4), y hay dos funciones, std :: begin y end, que están sobrecargados para manejar matrices o para seleccionar métodos de inicio/finalización. Esto me lleva a pensar en función de aceptar una secuencia genérica podría escribirse para que coincida con un variado-por el comportamiento del bucle:A partir genéricamente, más decltype teniendo en cuenta la declaración de uso local

template<class C> 
void f(C &c) { 
    using std::begin; 
    using std::end; 
    do_something_with(begin(c), end(c)); 
} 

Si hay un "más específica" Introducción/fin en el espacio de nombres de C, se seleccionará a través de ADL; de lo contrario, el código "predeterminado" es std :: begin/end.

Sin embargo, hay un motivo variado, ya que tiene esa excepción especial. Si pasar una matriz de un tipo en un espacio de nombres con un comenzar/final semánticamente diferente que toma un puntero, las formas de matriz de std :: comenzar/final no son seleccionados:

namespace ns { 
    struct A {}; 
    void begin(A*); // Does something completely different from std::begin. 
} 

void f_A() { // Imagine above f() called with an array of ns::A objects. 
    ns::A c[42]; 
    using std::begin; 
    begin(c); // Selects ns::begin, not array form of std::begin! 
} 

Para evitar esto, ¿Existe una solución mejor que escribir mis propios envoltorios de inicio/finalización (que usan ADL internamente) y llamarlos explícitamente en lugar de std :: begin o ADLized begin?

namespace my { 
    template<class T> 
    auto begin(T &c) // Also overload on T const &c, as std::begin does. 
    -> decltype(...) // See below. 
    { 
    using std::begin; 
    return begin(c); 
    } 

    template<class T, int N> 
    T* begin(T (&c)[N]) { 
    return c; 
    } 
} 
// my::end omitted, but it is analogous to my::begin. 

template<class C> 
void f(C &c) { 
    do_something_with(my::begin(c), my::end(c)); 
} 

Sin embargo, como se muestra por las elipsis anteriores, ni siquiera sé cómo escribir mi :: comenzar! ¿Cómo puedo, para ese decltype, seleccionar el tipo que se seleccionará a través de una declaración de uso local y ADL?

+0

Tardé unos 30 minutos en pensar y escribir esta pregunta, pero es algo que se ha estado gestando en mi cabeza por un tiempo, y creo que escribirlo me dio una idea para resolver el "decltype considerando el uso local- declaración ", pero no es bonita. Voy a auto-responder si nadie lo menciona alguna vez hoy. –

+0

Creo que hay otro problema: ¿no se consideraría :: :: begin para "return begin (c);" y, si se selecciona, ¿será recursión infinita? –

+0

@Fred: este segundo problema es simple y solo requiere un contenedor de función adicional en un espacio de nombres de "detalles" separado. –

Respuesta

3

que he encontrado la misma situación durante el uso de tuplas:

template<typename Tuple> 
auto f(Tuple&& tuple) 
-> /* ??? */ 
{ 
    using std::get; 
    return get<Idx>(tuple); 
} 

que acepta tanto std::tuple y boost::tuple, y acepta tanto lvalues ​​y rvalues ​​en contraposición a template<typename... Types> auto f(std::tuple<Types...>& tuple) -> /* ??? */.

Este caso particular fue resuelto con una clase de características, que de hecho es proporcionada por el estándar: std::tuple_element. Como es habitual en las clases de rasgos, la idea es que tuple sea un protocolo y cualquier cosa que quiera ajustarse a él proporcionará una especialización para, p. tuple_element. Entonces, en mi caso, la solución ya existía.

En su caso, si estuviera escribiendo una biblioteca, recomendaría escribir (y documentar) dicha clase de rasgos. En el código de la aplicación u otras situaciones, no estoy tan seguro.

+0

Los rasgos suenan como una buena idea, pero no estoy seguro de cómo aplicarlo a esta situación de una manera que no requiera una especialización por cada tipo que se pasará a mi :: inicio. –

1

Puede personalizar las matrices usted mismo. El tipo de una matriz es (y tiene que ser para iniciar/finalizar para trabajar) ElementType (&)[Size], por lo que si se sobrecarga la función como:

template<class C, size_t S> 
void f(C (&c)[S]) { 
    do_something_with(std::begin(c), std::end(c)); 
} 

debe comportarse gusta especialmente el bucle para.

En una nota lateral, que no es necesario std::begin y std::end a continuación, que son triviales:

template<class C, size_t S> 
void f(C (&c)[S]) { 
    do_something_with(c, c + S); 
} 

(pueden necesitar un yeso; yo en realidad solo lo usamos con las cosas que exigían punteros, ninguna iteradores).

Por otro lado, begin y end funciones que toman punteros son algo bastante tonto. Si el objeto puntiagudo es una colección, probablemente deberían estar tomando como referencia.

+0

Planeo hacer un caso especial de un parámetro array, pero al usar my :: begin, solo tengo que hacerlo una vez en vez de sobrecargar las funciones f, g, h, i, j ... En todas esas funciones, puedo solo llama a mi :: comenzar. –

+0

"las funciones de inicio y fin que toman punteros son algo bastante tonto" ¿Es tonto? Tal vez, pero ¿cómo puedo escribir una función ("f" en la pregunta) que no se rompa al azar (aún puede compilarse y ejecutarse) si alguien pasa un tipo en un espacio de nombres desconocido para mí que tiene tal un comienzo o un final? –

Cuestiones relacionadas