是否有任何技巧使用std :: cin来初始化一个constvariables?

常见的std :: cin用法

int X; cin >> X; 

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

天真的解决scheme

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

你可以通过改变X来const&来改善它。 仍然可以修改原来的variables。

我正在寻找如何做到这一点的简短而巧妙的解决scheme。 我相信我不是唯一能从这个问题的答案中得到好处的人。

//编辑:我想解决scheme很容易扩展到其他types(比方说,所有PODs, std::string和可移动的可复制类与微不足道的构造函数)(如果它没有意义,请让我知道评论)。

我可能会select返回一个optional ,因为stream可能会失败。 要testing它是否(如果您想分配另一个值),请使用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; } 

现场示例。

为了进一步确保当T不是inputstream时,用户不会得到超负荷的墙,你可以编写一个trait类来检查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; } 

现场示例。

我使用了一个detail::do_stream函数,因为否则s >> x仍然会在get_stream被parsing,并且当static_assert触发时,你仍然会得到我们想要避免的重载墙。 将这个操作委托给一个不同的函数使得这个工作。

对于这种情况,你可以使用lambdaexpression式:

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

(注意最后的extra())。

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

编辑:因为在评论中指出,这违背了DRY规则,所以可以利用auto5.1.2:4来减lesstypes重复:

5.1.2:4个状态:

[…]如果lambdaexpression式不包含尾随返回types,就好像尾随返回types表示以下types:

  • 如果复合语句的forms

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

    在左值到右值转换(4.1),数组到指针转换(4.2)和函数到指针转换(4.3)之后的返回expression式的types。

  • 否则,无效。

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

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

我不能决定是否更好,因为types现在“隐藏”在lambda体内…

编辑2:在注释中指出,只是删除可能的types名称,不会导致“DRY-correct”代码。 此外,这种情况下的追溯型扣除实际上是MSVC ++以及g ++的扩展,而不是(标准)。

对lx。的lambda解决scheme稍作调整:

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

严重干扰违规; 可以通过将const int x更改为const auto x来完全消除const auto x

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

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

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

请注意,这仍然可能低于lx。lambda的效率,因为这可以从NRVO中获益,而这仍然需要使用移动构造函数。 另一方面,优化编译器应该能够优化出一个无副作用的移动。

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

 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); 

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

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

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

我假设你将要初始化一个全局variables,因为对于一个局部variables来说,放弃三行简单易懂的语句以获得一个可疑的常量是一个非常尴尬的select。

在全球范围内,我们不能在初始化中出现错误,所以我们必须以某种方式处理它们。 这里有一些想法。

首先,一个模板化的小施工帮手:

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

请注意,全局初始化器不能抛出exception(在std::terminate痛苦之下),并且input操作可能会失败。 总而言之,以这种方式从用户input初始化全局variables可能是非常糟糕的devise。 也许会出现一个致命的错误:

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

只是在经过一些讨论后澄清我的立场:在当地范围内,我决不会诉诸这样一个尴尬的拐杖。 由于我们正在处理外部的,用户提供的数据,因此我们基本上必须将失败作为正常控制stream程的一部分:

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

我把它留给你来决定,这太写了,太难读了。