有没有使用std :: cin来初始化const变量的技巧?

Bar*_*icz 43 c++ iostream c++11

常见的std :: cin用法

int X;
cin >> X;
Run Code Online (Sandbox Code Playgroud)

这样做的主要缺点是X不能const.它很容易引入错误; 我正在寻找一些技巧来创建一个const值,并只写一次.

天真的解决方案

// Naive
int X_temp;
cin >> X_temp;
const int X = X_temp;
Run Code Online (Sandbox Code Playgroud)

你可以通过改变X来明显改善它const&; 仍然可以修改原始变量.

我正在寻找一个如何做到这一点的简短而聪明的解决方案.我相信我不是唯一一个能从这个问题的答案中获益的人.

//编辑:我希望解决方案可以轻松扩展到其他类型(比方说,所有POD,std::string以及带有普通构造函数的可移动可复制类)(如果没有意义,请在评论中告诉我) .

Xeo*_*Xeo 22

我可能会选择返回optional,因为流媒体可能会失败.要测试它是否(如果您想要分配另一个值),请使用get_value_or(default),如示例中所示.

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;
}
Run Code Online (Sandbox Code Playgroud)

实例.

为了进一步确保用户在没有T输入流传输时不会出现重载墙,您可以编写一个特征类来检查是否stream >> T_lvalue有效以及static_assert是否有效:

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;
}
Run Code Online (Sandbox Code Playgroud)

实例.

我正在使用一个detail::do_stream函数,因为否则s >> x仍会在内部解析get_stream,你仍然会得到我们想要在static_assert火灾时避免的重载墙.将此操作委派给不同的功能可以使这项工作成功.

  • @Xeo:这有些诙谐.我非常感谢解决方案的精妙之处.我很高兴看到有多大的解决方案,初始化`X`的看似简单的愿望已经沉淀下来:-) (5认同)
  • @KonradRudolph:这是定义`怪异`的好方法,就像帖子是'overkill`的一个很好的例子:-) (3认同)
  • @KonradRudolph:你用可选的const做什么?!"这是一个常数......也许"? (3认同)
  • 我声称这是唯一可接受的解决方案.但是,我很奇怪. (2认同)

lx.*_*lx. 18

你可以在这种情况下使用lambdas:

   const int x = []() -> int {
                     int t;
                     std::cin >> t;
                     return t;
                 }();
Run Code Online (Sandbox Code Playgroud)

(注意末尾的额外()).

这不是编写单独的函数,而是在读取代码时不必在源文件中跳转.

编辑:因为在评论中声明这违反了DRY规则,您可以利用auto5.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),数组到指针转换(4.2)和函数到指针转换(4.3)之后返回表达式的类型;

  • 否则,无效.

所以我们可以改变代码看起来像这样:

   const auto x = [] {
                     int t;
                     std::cin >> t;
                     return t;
                  }();
Run Code Online (Sandbox Code Playgroud)

我无法决定这是否更好,因为该类型现在已经"隐藏"在lambda体内...

编辑2:在评论中指出,只是删除可能的类型名称,不会导致"干燥正确"的代码.此外,在这种情况下,尾随返回类型推导实际上是MSVC++以及g ++和(尚未)标准的扩展.

  • @lx.它在5.1.2:1中的语法中;*lambda-declarator*是可选的,但是如果包含它,它必须在可选*trailing-return-type*之前包含*parameter-declaration-clause*.省略的*lambda-declarator*或省略的*trailing-return-type*的语义在5.1.2:4中定义. (2认同)
  • DRY的真正精神是将模式重构为一个函数,以便最终得到例如`const auto i = extract <int>(std :: cin);`,而不是你使模式更短. (2认同)

eca*_*mur 12

略微调整lx.的lambda解决方案:

const int x = [](int t){ return iss >> t, t; }({});
Run Code Online (Sandbox Code Playgroud)

显着减少DRY违规; 可以通过const int x改为const auto x:

const auto x = [](int t){ return iss >> t, t; }({});
Run Code Online (Sandbox Code Playgroud)

进一步改进; 你可以将副本转换为移动,否则逗号运算符会在12.8:31中抑制优化(移动构造函数被逗号运算符抑制):

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

请注意,这仍然可能比lx.的lambda效率低,因为它可以从NRVO中受益,而这仍然必须使用移动构造函数.另一方面,优化编译器应该能够优化非副作用的移动.


ron*_*chn 5

您可以调用函数来返回结果并在同一语句中初始化:

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);
Run Code Online (Sandbox Code Playgroud)

示例:http://ideone.com/kFBpT

如果您有C++ 11,那么如果您使用auto&&关键字,则只能指定一次类型.

auto&& X = in_get<int>();
Run Code Online (Sandbox Code Playgroud)


Jon*_*Mee 5

当然你可以做到这一点,只是构建一个临时的istream_iterator. 例如:

const auto X = *istream_iterator<int>(cin)
Run Code Online (Sandbox Code Playgroud)

值得在这里指出的是,当您这样做时,您将放弃所有错误检查的希望。通常从用户那里获取输入不会被认为是最明智的......但是,嘿,也许你以某种方式策划了这个输入?

Live Example