Common std::cin usage
int X;
cin >> X;
The main disadvantage of this is that X cannot be const. It can easily introduce bugs; and I am looking for some trick to be able to create a const value, and write to it just once.
The naive solution
// Naive
int X_temp;
cin >> X_temp;
const int X = X_temp;
You could obviously improve it by changing X to const&; still, the original variable can be modified.
I'm looking for a short and clever solution of how to do this. I am sure I am not the only one who will benefit from a good answer to this question.
// EDIT: I'd like the solution to be easily extensible to the other types (let's say, all PODs, std::string and movable-copyable classes with trivial constructor) (if it doesn't make sense, please let me know in comments).
I'd probably opt for returning an optional, since the streaming could fail. To test if it did (in case you want to assign another value), use get_value_or(default), as shown in the example.
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;
}
To further ensure that the user gets no wall-of-overloads presented when T is not input-streamable, you can write a trait class that checks if stream >> T_lvalue is valid and static_assert if it's not:
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;
}
I'm using a detail::do_stream function, since otherwise s >> x would still be parsed inside get_stream and you'd still get the wall-of-overloads that we wanted to avoid when the static_assert fires. Delegating this operation to a different function makes this work.
You could make use of lambdas for such cases:
const int x = []() -> int {
int t;
std::cin >> t;
return t;
}();
(Note the extra () at the end).
Instead of writing a separate functions, this has the advantage of not having to jump around in your source file, when reading the code.
Edit: Since in the comments it was stated that this goes against the DRY rule, you could take advantage of auto and 5.1.2:4 to reduce type repetition:
5.1.2:4 states:
[...] If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the following type:
if the compound-statement is of the form
{ attribute-specifier-seq(opt) return expression ; }the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion (4.2), and function-to-pointer conversion (4.3);
otherwise, void.
So we could alter the code to look like this:
const auto x = [] {
int t;
std::cin >> t;
return t;
}();
I can't decide if that is better though, since the type is now "hidden" within the lambda body...
Edit 2: In the comments it was pointed out, that just removing the type name where it is possible, does not result in a "DRY-correct" code. Also the trailing-return-type deduction in this case is currently actually an extension of MSVC++ as well as g++ and not (yet) standard.
A slight tweak to lx.'s lambda solution:
const int x = [](int t){ return iss >> t, t; }({});
Significantly less DRY violation; can be eliminated entirely by changing const int x to const auto x:
const auto x = [](int t){ return iss >> t, t; }({});
One further improvement; you can convert the copy into a move, since otherwise the comma operator suppresses the optimisation in 12.8:31 (Move constructor suppressed by comma operator):
const auto x = [](int t){ return iss >> t, std::move(t); }({});
Note that this is still potentially less efficient than lx.'s lambda, as that can benefit from NRVO whereas this still has to use a move constructor. On the other hand an optimising compiler should be able to optimise out a non-side-effect-bearing move.
You can call a function to return the result and initialize in the same statement:
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);
Example: http://ideone.com/kFBpT
If you have C++11, then you can specify the type only once if you use the auto&& keyword.
auto&& X = in_get<int>();
I'm assuming that you will want to initialize a global variable, since for a local variable it just seems like a very awkward choice to forgo three lines of plain and understandable statements in order to have a constant of questionable value.
At the global scope, we can't have errors in the initialization, so we'll have to handle them somehow. Here are some ideas.
First, a templated little construction helper:
template <typename T>
T cinitialize(std::istream & is) noexcept
{
T x;
return (is && is >> x) ? x : T();
}
int const X = cinitialize<int>(std::cin);
Note that global initializers must not throw exceptions (under pain of std::terminate), and that the input operation may fail. All told, it's probably pretty bad design to initialize global variables from user input in such a fashion. Perhaps a fatal error would be indicated:
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;
}
Just to clarify my position after some discussion in the comments: In a local scope I would never resort to such an awkward crutch. Since we're processing external, user-supplied data, we basically have to live with failure as part of the normal control flow:
void foo()
{
int x;
if (!(std::cin >> x)) { /* deal with it */ }
}
I leave it up to you to decide whether that's too much to write or too hard too read.
Sure you can do this be just constructing a temporary istream_iterator. For example:
const auto X = *istream_iterator<int>(cin)
It's worth pointing out here that you're abandoning all hope of error checking when you do this. Which generally in the taking of input from a user would not be considered the wisest... but hey, maybe you've curated this input somehow?
来源:https://stackoverflow.com/questions/12279601/are-there-any-tricks-to-use-stdcin-to-initialize-a-const-variable