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
Can Class Template Constructors Have a Redundant Template Parameter List in C++20
Why Use Prefixes on Member Variables in C++ Classes
Converting from Signed Char to Unsigned Char and Back Again
How to Typically/Always Use Std::Forward Instead of Std::Move
What Destructors Are Run When the Constructor Throws an Exception
How to Extract the Source Filename Without Path and Suffix at Compile Time
Dynamically Allocated Memory After Program Termination
How to Change the Background Color of a Button Winapi C++
Easiest Way to Rotate by 90 Degrees an Image Using Opencv
How Bad Is Redefining/Shadowing a Local Variable
Detailed Guide on Using Gcov with Cmake/Cdash
How to Download the Visual C++ Command Line Compiler Without Visual Studio
Can a Cast Operator Be Explicit
How Can Std::Make_Heap Be Implemented While Making at Most 3N Comparisons