Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
585 views
in Technique[技术] by (71.8m points)

c++ - 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).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...