such libreria ejemplos descargar c++ c++11 iostream

c++ - libreria - ¿Hay algún truco para usar std:: cin para inicializar una variable const?



iostream vs stdio (6)

Uso común de std :: cin

int X; cin >> X;

La principal desventaja de esto es que X no puede ser const . Puede introducir errores fácilmente; y estoy buscando algún truco para poder crear un valor constante y escribir en él solo una vez.

La solución ingenua

// Naive int X_temp; cin >> X_temp; const int X = X_temp;

Obviamente, podrías mejorarlo cambiando X por const& ; aún así, la variable original puede ser modificada.

Estoy buscando una solución corta e inteligente de cómo hacer esto. Estoy seguro de que no soy el único que se beneficiará de una buena respuesta a esta pregunta.

// EDITAR: Me gustaría que la solución fuera fácilmente extensible a los otros tipos (digamos, todos los PODs, std::string y clases copiables con un constructor trivial) (si no tiene sentido, déjeme saber en comentarios).


Claro que puedes hacer esto simplemente construyendo un istream_iterator temporal. Por ejemplo:

const auto X = *istream_iterator<int>(cin)

Vale la pena señalar aquí que está abandonando toda esperanza de comprobación de errores cuando hace esto. ¿Qué cosa general en la toma de información de un usuario no se consideraría la más inteligente ... pero bueno, tal vez haya curado esta información de alguna manera?

Ejemplo vivo


Podrías hacer uso de lambdas para tales casos:

const int x = []() -> int { int t; std::cin >> t; return t; }();

(Note el extra () al final).

En lugar de escribir funciones separadas, esto tiene la ventaja de no tener que saltar en el archivo fuente al leer el código.

Edit: Como en los comentarios se dijo que esto va en contra de la regla DRY, podría aprovechar auto y 5.1.2:4 para reducir la repetición de tipos:

5.1.2:4 estados:

[...] Si una expresión lambda no incluye un tipo de retorno-retorno, es como si el tipo-retorno-de-retorno denota el siguiente tipo:

  • si la declaración compuesta es de la forma

    { attribute-specifier-seq(opt) return expression ; }

    el tipo de expresión devuelta después de la conversión de valor a rvalor (4.1), conversión de matriz a puntero (4.2) y conversión de función a puntero (4.3);

  • de lo contrario, nulo.

Así podríamos alterar el código para que se vea así:

const auto x = [] { int t; std::cin >> t; return t; }();

No puedo decidir si eso es mejor, ya que el tipo ahora está "oculto" dentro del cuerpo lambda ...

Edición 2: En los comentarios se señaló que solo eliminar el nombre de tipo donde sea posible, no da como resultado un código "SECO-correcto". Además, la deducción del tipo de retorno final en este caso es actualmente una extensión de MSVC ++, así como g ++ y no (aún) estándar.


Probablemente optaría por devolver un optional , ya que la transmisión podría fallar. Para probar si lo hizo (en caso de que desee asignar otro valor), use get_value_or(default) , como se muestra en el ejemplo.

template<class T, class Stream> boost::optional<T> stream_get(Stream& s){ T x; if(s >> x) return std::move(x); // automatic move doesn''t happen since // return type is different from T return boost::none; }

Ejemplo vivo.

Para asegurarse aún más de que el usuario no reciba ninguna pared de sobrecargas cuando T no es de stream >> T_lvalue entrada, puede escribir una clase de rasgo que verifique si la stream >> T_lvalue es válida y static_assert si no lo es:

namespace detail{ template<class T, class Stream> struct is_input_streamable_test{ template<class U> static auto f(U* u, Stream* s = 0) -> decltype((*s >> *u), int()); template<class> static void f(...); static constexpr bool value = !std::is_void<decltype(f<T>(0))>::value; }; template<class T, class Stream> struct is_input_streamable : std::integral_constant<bool, is_input_streamable_test<T, Stream>::value> { }; template<class T, class Stream> bool do_stream(T& v, Stream& s){ return s >> v; } } // detail:: template<class T, class Stream> boost::optional<T> stream_get(Stream& s){ using iis = detail::is_input_streamable<T, Stream>; static_assert(iis::value, "T must support ''stream >> value_of_T''"); T x; if(detail::do_stream(x, s)) return std::move(x); // automatic move doesn''t happen since // return type is different from T return boost::none; }

Ejemplo vivo.

Estoy usando una función detail::do_stream , ya que de lo contrario s >> x aún se analizaría dentro de get_stream y todavía obtendríamos las sobrecargas de la pared que queríamos evitar cuando se static_assert . Delegar esta operación a una función diferente hace que esto funcione.


Puede llamar a una función para devolver el resultado e inicializar en la misma declaración:

template<typename T> const T in_get (istream &in = std::cin) { T x; if (!(in >> x)) throw "Invalid input"; return x; } const int X = in_get<int>(); const string str = in_get<string>(); fstream fin("myinput.in",fstream::in); const int Y = in_get<int>(fin);

Ejemplo: http://ideone.com/kFBpT

Si tiene C ++ 11, puede especificar el tipo solo una vez si usa la palabra clave auto&& .

auto&& X = in_get<int>();


Supongo que querrá inicializar una variable global , ya que para una variable local parece una opción muy incómoda renunciar a tres líneas de afirmaciones claras y comprensibles para tener una constante de valor cuestionable.

En el ámbito global, no podemos tener errores en la inicialización, por lo que tendremos que manejarlos de alguna manera. Aquí hay algunas ideas.

Primero, un pequeño ayudante de construcción con plantilla:

template <typename T> T cinitialize(std::istream & is) noexcept { T x; return (is && is >> x) ? x : T(); } int const X = cinitialize<int>(std::cin);

Tenga en cuenta que los inicializadores globales no deben lanzar excepciones (bajo el dolor de std::terminate ), y que la operación de entrada puede fallar. Dicho todo esto, es probablemente un diseño bastante malo para inicializar variables globales a partir de las aportaciones de los usuarios de esa manera. Quizás se indique un error fatal:

template <typename T> T cinitialize(std::istream & is) noexcept { T x; if (!(is && is >> x)) { std::cerr << "Fatal error while initializing constants from user input./n"; std::exit(1); } return x; }

Solo para aclarar mi posición después de una discusión en los comentarios: En el ámbito local, nunca recurriría a una muleta tan incómoda. Como estamos procesando datos externos proporcionados por el usuario , básicamente tenemos que vivir con fallas como parte del flujo de control normal:

void foo() { int x; if (!(std::cin >> x)) { /* deal with it */ } }

Lo dejo a usted decidir si es demasiado para escribir o demasiado para leer.


Un ligero retoque a la solución lambda de lx.

const int x = [](int t){ return iss >> t, t; }({});

Significativamente menos violación de SECO; puede eliminarse por completo cambiando const int x por const auto x :

const auto x = [](int t){ return iss >> t, t; }({});

Una mejora adicional; puede convertir la copia en un movimiento, ya que de lo contrario el operador de coma suprime la optimización en 12.8: 31 ( constructor de movimiento suprimido por el operador de coma ):

const auto x = [](int t){ return iss >> t, std::move(t); }({});

Tenga en cuenta que esto todavía es potencialmente menos eficiente que la lambda de lx., Ya que puede beneficiarse de NRVO mientras que todavía tiene que usar un constructor de movimientos. Por otro lado, un compilador de optimización debería ser capaz de optimizar un movimiento sin efecto secundario.