2012-07-01 7 views
10

tengo código como este¿Por qué no range-para encontrar mis sobrecargas de inicio y fin para std :: istream_iterator?

std::ifstream file(filename, std::ios_base::in); 
if(file.good()) 
{ 
    file.imbue(std::locale(std::locale(), new delimeter_tokens())); 
    for(auto& entry : std::istream_iterator<std::string>(file)) 
    { 
     std::cout << entry << std::endl;  
    } 
} 
file.close(); 

donde std::istream_iterator<std::string> 's begin() y end() se definen como sigue

template<class T> 
std::istream_iterator<T> begin(std::istream_iterator<T>& stream) 
{ 
    return stream; 
} 

template<class T> 
std::istream_iterator<T> end(std::istream_iterator<T>& stream) 
{ 
    return std::istream_iterator<T>(); 
} 

que es lo Mark Nelson también ha escrito en Dr. Dobb here. Por desgracia, el código falla al compilar en mi Visual Studio 2012 con mensajes de error

error C3312: ninguna función se puede llamar 'comenzará encontrado para el tipo 'std :: istream_iterator < _Ty>'

y

error C3312: ninguna función se puede llamar 'fin' encontrado para el tipo 'std :: istream_iterator < _Ty>'

Pregunta: ¿Hay algo que no haya notado, error en el compilador (poco probable, pero por las dudas) o ... Bueno, ¿alguna idea?


Estas preguntas se limpian considerablemente, según lo aconsejado por Xeo. Para proporcionar más antecedentes y referencias relacionadas con mi other question en Stackoverflow, me preguntaba cómo hacer que el análisis basado en líneas sea más limpio que los bucles habituales. Un poco de codificación y comprobación de la Internet, y yo tenía un bosquejo de trabajo de la siguiente manera

std::ifstream file(filename, std::ios_base::in); 
if(file.good()) 
{    
    file.imbue(std::locale(std::locale(), new delimeter_tokens())); 
    for(auto& entry : istream_range<std::string>(file) 
    { 
     std::cout << entry << std::endl;  
    } 
} 
file.close(); 

pero no había ligero inconveniente Traté de poner remedio. Creo que sería un aspecto más natural de escribir como en el código que falla al compilar y no como

for(auto& entry : istream_range<std::string>(file) 

Por favor, tome nota de los diferentes iterador. El delimeter_tokens se define como Nawaz amablemente ha mostrado here (código no duplicado) y istream_range como en el blog de Code Synthesis here. Creo que las implementaciones de inicio y fin deberían funcionar, como se anuncia en la publicación de blog Code Synthesis

La última regla (la alternativa a las funciones autónomas begin() y end) nos permite adaptar de forma no invasiva un contenedor existente a la interfaz de bucle basada en rango.

Así mi pregunta con todos los (ir) antecedentes relevantes.

+0

he tomado la libertad de cambiar el título de la pregunta para reflejar la pregunta real pedido. Deshacer o cambiar si es necesario. – Xeo

+0

Es mejor así, creo. Llama la atención de aquellos que buscan una solución similar a la mía y aquellos que tienen el problema de búsqueda para resolver. ¿Cómo puedo hacer que su publicación sea una respuesta (o alguien más capacitado lo hace)? – Veksi

+0

¡Gracias por eso también! : D – Veksi

Respuesta

7

Ranged-por depende de ADL si el manejo especial para matriz nativa (T foo[N]) y el miembro begin/end no arroja ningún resultado.

§6.5.4 [stmt.ranged] p1

  • lo contrario, comenzar-expr y extremo expr son begin(__range) y end(__range), respectivamente, , donde begin y end se buscan con búsqueda argumento-dependiente (3.4.2). A los efectos de esta búsqueda de nombres, el espacio de nombres std es un espacio de nombres asociado.

Su problema es que el espacio de nombres asociado de std::istream_iterator es (obviamente) namespace std, no el espacio de nombres global.

§3.4.2 [basic.lookup.argdep] p2

Para cada tipo de argumento T en la llamada de función, hay un conjunto de cero o más espacios de nombres asociados y un conjunto de cero o más asociados clases para ser considerado. Los conjuntos de espacios de nombres y clases están determinados por completo por los tipos de argumentos de función [...].

  • Si T es un tipo fundamental, sus conjuntos de espacios de nombres y clases asociados están vacíos.
  • Si T es un tipo de clase (incluidas las uniones), sus clases asociadas son: la clase en sí; la clase de la que es miembro, si la hay; y sus clases base directas e indirectas. Sus espacios de nombres asociados son los espacios de nombre de los cuales son miembros sus clases asociadas. Además, si T es una especialización de plantilla de clase, sus espacios de nombres y clases asociados también incluyen: los espacios de nombres y las clases asociados con los tipos de argumentos de plantilla proporcionados para los parámetros de tipo de plantilla [...].

Observe la última parte (citada) de la segunda viñeta. Básicamente significa que el uso de una clase que es un miembro del espacio de nombres global como el argumento de la plantilla hace que el trabajo código:

#include <iterator> 
#include <iostream> 

template<class T> 
std::istream_iterator<T> begin(std::istream_iterator<T> is){ 
    return is; 
} 
template<class T> 
std::istream_iterator<T> end(std::istream_iterator<T>){ 
    return std::istream_iterator<T>(); 
} 

struct foo{}; 

std::istream& operator>>(std::istream& is, foo){ 
    return is; 
} 

int main(){ 
    for(foo f : std::istream_iterator<foo>(std::cin)) 
    //        ^^^ 
    // make global namespace one of the associated namespaces 
    ; 
} 
+0

No puedo seguirte ahora. ¿Cómo funcionaría ese truco de asociación de espacio de nombres si debería leer cadenas tokenizadas de la secuencia de archivos? Es decir, el foo que señalas en el bucle debería ser cuerdas en mi caso. – Veksi

+0

@Veksi: Básicamente, solo la primera parte de mi respuesta es importante: no se pueden poner sobrecargas para los miembros 'std' en el espacio de nombres global de ranged-for. La segunda parte fue solo para mostrar una peculiaridad de ADL que permitiría que eso funcionara. : P – Xeo

+1

Al comentar sobre [esta Q & A] (http://stackoverflow.com/a/18321288/819272), descubrí acerca de [* core issue * 1442] (http://wg21.cmeerw.net/cwg/ issue1442) que cambia las reglas un poco. Namespace 'std' ya no es un espacio de nombres asociado y [* se menciona explícitamente que *] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3691.pdf)" begin y end se buscan en los espacios de nombres asociados (3.4.2). [Nota: la búsqueda no calificada ordinaria (3.4.1) no se realiza. - end note] " – TemplateRex

1

Debido a que el argumento depende de la búsqueda, el compilador intenta encontrar begin() y end() en el espacio de nombres std. Si coloca sus funciones allí, el código compila.

Como la búsqueda de nombres es un problema complicado en C++, no estoy del todo seguro de si el compilador se comporta correctamente o no.

+1

No está permitido colocar sobrecargas en 'namespace std'. – Xeo

+0

Pero puedes especializar plantillas, ¿no? – jrok

+2

@jrok: Sí, pero solo para los tipos definidos por el usuario, no para los tipos que se originan de 'std' (IIRC). Además, no se puede * parcialmente * especializar las plantillas de funciones y la especialización de plantillas de funciones en general está mal visto. – Xeo

Cuestiones relacionadas