是否有任何技巧使用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规则,所以可以利用 auto
和5.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 */ } }
我把它留给你来决定,这太写了,太难读了。