Are there any tricks to use std::cin to initialize a const variable?
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;
}
Live example.
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;
}
Live example.
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.