Rationale
I try to avoid assignments in C++ code completely. That is, I use only initialisations and declare local variables as const
whenever possible (i.e. always except for loop variables or accumulators).
Now, I’ve found a case where this doesn’t work. I believe this is a general pattern but in particular it arises in the following situation:
Problem Description
Let’s say I have a program that loads the contents of an input file into a string. You can either call the tool by providing a filename (tool filename
) or by using the standard input stream (cat filename | tool
). Now, how do I initialise the string?
The following doesn’t work:
bool const use_stdin = argc == 1;
std::string const input = slurp(use_stdin ? static_cast<std::istream&>(std::cin)
: std::ifstream(argv[1]));
Why doesn’t this work? Because the prototype of slurp
needs to look as follows:
std::string slurp(std::istream&);
That is, the argument i non-const
and as a consequence I cannot bind it to a temporary. There doesn’t seem to be a way around this using a separate variable either.
Ugly Workaround
At the moment, I use the following solution:
std::string input;
if (use_stdin)
input = slurp(std::cin);
else {
std::ifstream in(argv[1]);
input = slurp(in);
}
But this is rubbing me the wrong way. First of all it’s more code (in SLOCs) but it’s also using an if
instead of the (here) more logical conditional expression, and it’s using assignment after declaration which I want to avoid.
Is there a good way to avoid this indirect style of initialisation? The problem can likely be generalised to all cases where you need to mutate a temporary object. Aren’t streams in a way ill-designed to cope with such cases (a const
stream makes no sense, and yet working on a temporary stream does make sense)?