C++ 使用std::cin初始化常量变量有什么技巧吗?
常见性病::cin用法C++ 使用std::cin初始化常量变量有什么技巧吗?,c++,c++11,iostream,C++,C++11,Iostream,常见性病::cin用法 int X; cin >> X; 这样做的主要缺点是X不能是const。它很容易引入bug;我正在寻找一些技巧,能够创建一个常量值,并且只写一次 天真的解决方案 // Naive int X_temp; cin >> X_temp; const int X = X_temp; 通过将X更改为const&,显然可以改进它;不过,可以修改原始变量 我正在寻找一个简短而聪明的解决方案。我相信我不是唯一一个能从这个问题的好答案中获益的人 //编辑:我希
int X;
cin >> X;
这样做的主要缺点是X不能是const
。它很容易引入bug;我正在寻找一些技巧,能够创建一个常量值,并且只写一次
天真的解决方案
// Naive
int X_temp;
cin >> X_temp;
const int X = X_temp;
通过将X更改为const&
,显然可以改进它;不过,可以修改原始变量
我正在寻找一个简短而聪明的解决方案。我相信我不是唯一一个能从这个问题的好答案中获益的人
//编辑:我希望该解决方案能够轻松扩展到其他类型(比如说,所有pod、
std::string
和带有普通构造函数的可移动可复制类)(如果没有意义,请在注释中告诉我)。我假设您将要初始化一个全局变量,因为对于一个局部变量来说,放弃三行简单易懂的语句以获得一个有问题的常数似乎是一个非常尴尬的选择
在全局范围内,初始化中不能有错误,因此我们必须以某种方式处理它们。这里有一些想法
首先,一个模板化的小型构造助手:
template <typename T>
T cinitialize(std::istream & is) noexcept
{
T x;
return (is && is >> x) ? x : T();
}
int const X = cinitialize<int>(std::cin);
在评论中经过一些讨论后,我想澄清一下我的立场:在地方范围内,我永远不会求助于这样一个笨拙的拐杖。由于我们正在处理外部的、用户提供的数据,我们基本上不得不接受故障作为正常控制流的一部分:
void foo()
{
int x;
if (!(std::cin >> x)) { /* deal with it */ }
}
我让您来决定这是太难写还是太难读。您可以调用函数返回结果并在同一语句中初始化:
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);
在这种情况下,您可以使用lambdas:
const int x = []() -> int {
int t;
std::cin >> t;
return t;
}();
(请注意结尾处的额外()
与编写单独的函数不同,它的优点是在读取代码时不必在源文件中跳转
编辑:由于在评论中指出这违反了干式规则,您可以利用auto
和5.1.2:4
来减少类型重复:
5.1.2:4
规定:
[…]如果lambda表达式不包含尾随返回类型,则如下所示
如果尾部返回类型表示以下类型:
- 如果复合语句的形式为
左值到右值转换后返回的表达式的类型(4.1), 数组到指针的转换(4.2)和函数到指针的转换(4.3){属性说明符seq(opt)返回表达式;}
- 否则,无效
const auto x = [] {
int t;
std::cin >> t;
return t;
}();
我无法决定这是否更好,因为类型现在“隐藏”在lambda主体中
编辑2:在评论中指出,只要在可能的情况下删除类型名称,不会产生“完全正确”的代码。
此外,本例中的后续返回类型扣减目前实际上是MSVC++和g++的扩展,而不是(尚未)标准。我可能会选择返回一个
可选的
,因为流式处理可能会失败。要测试是否有(如果您想分配另一个值),请使用get\u value\u或(默认值)
,如示例所示
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;
}
我正在使用一个
detail::do_stream
函数,因为否则s>>x
仍将在get_stream
中解析,并且当static_assert
触发时,您仍然会遇到我们想要避免的重载墙。将此操作委托给不同的函数可以实现此操作。对lx的lambda解决方案进行了一些微调:
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()中的优化:
请注意,这仍然可能比lx的lambda效率低,因为它可以从NRVO中获益,而这仍然需要使用移动构造函数。另一方面,优化编译器应该能够优化出无副作用的轴承移动。通过构造临时轴承,您确实可以做到这一点。例如:
const auto X = *istream_iterator<int>(cin)
const auto X=*istream\u迭代器(cin)
这里值得指出的是,当您这样做时,您放弃了所有错误检查的希望。一般来说,从用户那里获取信息并不是最明智的。。。但是,嘿,也许你策划了这一投入
将前两行设为返回
int
:)const int X=read_cin()代码>将const int
更改为const int&
不会改善任何情况。第二个与const int*const
几乎完全相同,因此您将复制sizeof(int*)
,而在const int
中,您将复制sizeof(int)
,因此数据量可能完全相同。引用int
没有任何意义-您可能不应该引用任何POD-s。我个人喜欢您提出的“幼稚解决方案”。您从用户处读取的值显然不是常量,然后您明确地将其值复制到另一个值中,您承诺不会通过将其标记为常量来更改该值。这有点难看,但似乎完全符合正在发生的事情。你的“天真”解决方案是如何做到这一点。没有人会因为执行如此简单、平凡的任务而从庞大的夸夸其谈的类接口中获益。如果您发现自己编写长而复杂的函数只是为了设置一个变量,那么这是一个特定的标志,表明程序设计中出现了严重错误。@BartekBanachewicz“这需要为我想读的每种类型编写一个函数。”然后将其设为read\u cin()
const int x = [](int t){ return iss >> t, t; }({});
const auto x = [](int t){ return iss >> t, t; }({});
const auto x = [](int t){ return iss >> t, std::move(t); }({});
const auto X = *istream_iterator<int>(cin)