Существуют ли какие-либо трюки для использования std :: cin для инициализации переменной const?

Общее использование std :: cin

int X; cin >> X; 

Основным недостатком этого является то, что X не может быть const . Он может легко вводить ошибки; и я ищу некоторый трюк, чтобы иметь возможность создать значение const и писать ему только один раз.

Наивное решение

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

Вы, очевидно, могли бы улучшить его, изменив X на const& ; Тем не менее исходная переменная может быть изменена.

Я ищу короткое и умное решение, как это сделать. Я уверен, что я не единственный, кто получит хороший ответ на этот вопрос.

// EDIT: я хотел бы, чтобы решение было легко расширяемым для других типов (скажем, все POD, std::string и movable-copyable classes с тривиальным конструктором) (если это не имеет смысла, пожалуйста, позвольте мне знать в комментариях).

Вероятно, я предпочел бы вернуть optional , поскольку streamовая передача может завершиться неудачей. Чтобы проверить, было ли это (если вы хотите назначить другое значение), используйте get_value_or(default) , как показано в примере.

 template boost::optional 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; } 

Живой пример.

Чтобы еще больше убедиться, что пользователь не получает никаких перегрузок, представленных в том случае, если T не является streamом данных, вы можете написать class признаков, который проверяет, действительно ли stream >> T_lvalue и static_assert если это не так:

 namespace detail{ template struct is_input_streamable_test{ template static auto f(U* u, Stream* s = 0) -> decltype((*s >> *u), int()); template static void f(...); static constexpr bool value = !std::is_void(0))>::value; }; template struct is_input_streamable : std::integral_constant::value> { }; template bool do_stream(T& v, Stream& s){ return s >> v; } } // detail:: template boost::optional stream_get(Stream& s){ using iis = detail::is_input_streamable; 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; } 

Живой пример.

Я использую функцию detail::do_stream , так как иначе s >> x все равно будет разбираться внутри get_stream и вы все равно получите перегрузку, которую мы хотели бы избежать, когда static_assert . Делегирование этой операции на другую функцию делает эту работу.

Вы можете использовать lambda для таких случаев:

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

(Обратите внимание на дополнительный () в конце).

Вместо написания отдельных функций это имеет то преимущество, что вам не нужно прыгать в исходном файле при чтении кода.

Изменить: поскольку в комментариях было указано, что это противоречит правилу DRY, вы можете воспользоваться auto и 5.1.2:4 чтобы уменьшить повторение типа:

5.1.2:4 состояния:

[…] Если lambda-выражение не включает тип trailing-return-type, это похоже на то, что тип trailing-return-type обозначает следующий тип:

  • если составной оператор имеет вид

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

    тип возвращаемого выражения после преобразования lvalue-to-rvalue (4.1), преобразования от matrix к указателю (4.2) и преобразования функции в указатель (4.3);

  • в противном случае, void.

Поэтому мы могли бы изменить код, чтобы он выглядел так:

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

Я не могу решить, лучше ли это, потому что тип теперь «скрыт» внутри тела лямбды …

Изменить 2: в комментариях было указано, что просто удаление имени типа, где это возможно, не приводит к «правильному» правилу. Кроме того, вывод вывода trailing-return-type в этом случае на самом деле является расширением MSVC ++, а также g ++ и не (пока) стандартом.

Небольшая настройка lambda-решения lx.:

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

Значительно менее суровое нарушение; можно полностью устранить, изменив const int x на const auto x :

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

Еще одно улучшение; вы можете преобразовать копию в ход, поскольку в противном случае оператор запятой подавляет оптимизацию в 12.8: 31 ( Move constructor, подавленный оператором запятой ):

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

Обратите внимание, что это по-прежнему потенциально менее эффективно, чем lambda, так как это может выиграть от NRVO, тогда как это все еще должно использовать конструктор перемещения. С другой стороны, оптимизирующий компилятор должен иметь возможность оптимизировать перемещение без побочных эффектов.

Вы можете вызвать функцию для возврата результата и инициализации в том же самом выражении:

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

Пример: http://ideone.com/kFBpT

Если у вас есть C ++ 11, вы можете указать тип только один раз, если вы используете ключевое слово auto&& .

 auto&& X = in_get(); 

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

В глобальном масштабе мы не можем иметь ошибок в инициализации, поэтому нам придется как-то их обрабатывать. Вот некоторые идеи.

Во-первых, шаблонный помощник по строительству:

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

Обратите внимание, что глобальные инициализаторы не должны генерировать исключения (под болью std::terminate ) и что операция ввода может завершиться неудачей. Все сказанное, вероятно, довольно плохой дизайн, чтобы таким образом инициализировать глобальные переменные из пользовательского ввода. Возможно, будет указана фатальная ошибка:

 template  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; } 

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

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

Я оставляю это для вас, чтобы решить, слишком ли много писать или слишком сильно читать.

Конечно, вы можете сделать это, просто istream_iterator временный istream_iterator . Например:

 const auto X = *istream_iterator(cin) 

Здесь стоит отметить, что вы отказываетесь от всякой надежды на проверку ошибок, когда вы это делаете. Который вообще в принятии ввода от пользователя не считался самым мудрым … но эй, может быть, вы куратором этого ввода каким-то образом?

Живой пример

  • Как заставить cout вести себя как в двоичном режиме?
  • Пользовательский манипулятор для C ++ iostream
  • Как написать настраиваемый stream ввода в C ++
  • проверка доступности данных до вызова std :: getline
  • Какова реальная причина не использовать бит EOF в качестве условия извлечения streamа?
  • Можете ли вы объяснить концепцию streamов?
  • Поток streamа байтов и символов
  • Как я могу составить выходные streamи, поэтому вывод идет сразу несколькими местами?
  • Как правильно перегрузить оператор << для streamа?
  • Является ли стандарт C ++ низкой эффективностью для iostreams, или я просто имею дело с плохой реализацией?
  • Должен ли я использовать #include рядом с ?
  • Interesting Posts

    ElementName привязка из MenuItem в ContextMenu

    Как сделать Windows 7 полностью чувствительной к регистру в отношении файловой системы?

    Простая страница Bootstrap не реагирует на iPhone

    Обмен ссылкой на WhatsApp с мобильного сайта (а не приложения) для Android

    Как определить строковый литерал в командной строке gcc?

    Подписчики на графиках в R

    Последовательный GUID в Linq-to-Sql?

    Фрагмент или fragment поддержки?

    Разница между CSS-селектором и фильтром jQuery?

    Как я могу переопределить шаблон @ Html.LabelFor?

    Как я могу использовать файлы .xcconfig в Xcode 4?

    Показать текущее местоположение и соседние места и маршрут между двумя местами с помощью API Карт Google на Android

    Сколько места я должен зарезервировать для резервных копий Time Machine?

    Связывание / группировка файла с папкой в ​​Windows, так что они оба перемещаются вместе

    Как локально (в локальной сети) и публично (через Интернет) получить доступ к IP-камере, используя только один IP-адрес?

    Давайте будем гением компьютера.