Are There Any Tricks to Use Std::Cin to Initialize a Const Variable

Are there any tricks to use std::cin to initialize a const variable?

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.

Why can't I read a value from the user and make it a constant?

If you define the variable as

const int quantity;

you're saying "I'd like an int called quantity, and under no circumstances do I want its value to ever change." As a result, if you then write

cin >> quantity;

the compiler says something to the effect of "wait - you want me to change the value of quantity by replacing it with whatever the user entered, but earlier you said that you never wanted me to change it!"

My sense is that you wanted to make it so that after you give an initial value to quantity that value never changes, but with const variables that initial value needs to be set when the variable is created. You could therefore try something like this:

const int quantity = readValue();

for some function readValue() that reads and returns an int value. That way, the compiler sees that quantity is given a fixed value, it knows that the value never changes, and you never try to directly cin into the value of quantity.

For a more technical perspective on the errors you got: when the compiler read

const int quantity;

without any value assigned to it, it reported an error because it's unusual to create a constant without giving it a value. (I can see from your code that you meant to give it a value, but the way you did it wasn't legal and the compiler didn't piece the two things together). The second error about operator >> resulted because none of the different ways that you can read something from cin (read a string, read an int, read a char, etc.) applied, since each of them assumed that they can get a mutable (modifiable) view of the value in question. Again, both of these issues stem from the fact that the compiler saw your code as two separate independent errors rather than one large "oops, that's not how const works" error.

Can a variable be initialized with an istream on the same line it is declared?

The smart-ass answer:

int old; std::cin >> old;

The horrible answer:

int old, dummy = (std::cin >> old, 0);

The proper answer: old has to be defined with a declaration before it can be passed to operator>> as an argument. The only way to get a function call within the declaration of a variable is to place it in the initialization expression as above. The accepted way to declare a variable and read input into it is as you have written:

int old;
std::cin >> old;

How can I declare a constant data member but not initialize it until later?

You should not change a const value.

For what you trying to accomplish I'd recommend declaring a non-const pointer and a const pointer and assigning the non-const one to the const one after the initialization:

int _tmain(int argc, _TCHAR* argv[])
{
const int *tempTempInt = new int[4];
TryInitialize(tempInt);
const int* const tempInt = tempTempInt;
std::cout << tempInt[1] << endl; //this is now constant.

system("pause");

return 0;
}

Also pay attention where you put the const in the pointer declaration:

const int* const tempInt = tempTempInt;

In the declaration above the second const means that you cannot change the pointer; the first const means that you cannot change the pointed value(s).

Are there any tricks I can use to initialize global constants with a separate function call?

Do not use global variables. They make stuff confusing, like the problems you are having. Make it all local. Write getters and if needed setters. Force the user to call a function to get the configuration, where you do the stuff you want.

// settings.hpp
struct TheSettings {
int waitEventTimeoutSeconds;
};

const TheSettings& settings();

// settings.cpp
const TheSettings& settings() {
static TheSettings cachesettings;
static bool cacheready = false;
if (!cacheready) {
// open file
// construct settings
cachesettings.waitEventTimeoutSeconds = read_from_file;
cacheready = true;
}
return cachesettings;
}

// main.cpp
int main() {
// instead of `::` type `().`
std::cout << settings().waitEventTimeoutSeconds;
}

You might be interested in reseraching C++ Singleton design pattern and Static Initialization Order Fiasco .

How do I initialize a constant global variable from a runtime arguments?

Objects in global scope gets constructed before main() gets invoked, so it's logically impossible to use main's argc and argv to construct them. This is a fairly fundamental chicken-and-egg problem, and there is no portable way around it.

It might be possible to cheat and use function static scope to simulate it, something like:

class MySingleton {
public:
MySingle(int argc, char **argv);

// ...
}

const MySingleton &global_singleton(int argc=0, char **argv=nullptr)
{
static MySingleton instance{argc, argv};

return instance;
}

int main(int argc, char **argv)
{
global_singleton(argc, argv);
}

This might work as long as nothing else in global scope calls global_singleton in its constructor. main() does it ASAP, passing in the real argc and argv, and everyone else just calls it with the useless parameters defaulted.

Why is it not possible to cin to an auto declared variable?

The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.

The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.

You code could be made to work in many different ways, here's one that makes some sense:

std::string default_name = "";
auto name = default_name;

cin >> name;


Related Topics



Leave a reply



Submit