std::pair assigning first and second to semantically named variables

happy_marmoset picture happy_marmoset · Feb 7, 2014 · Viewed 7.5k times · Source

There is a very popular question about "std::pair vs struct with two fields". But I have a question about reassigning first and second values to semantically named variables. In regular scenario we have something like this:

const std::pair<const int, const int> result = processSomething();
std::cout << result.second << " of " << result.first << std::endl;

But what if we assign them to reference variables first:

const std::pair<const int, const int> result = processSomething();
const int &numTotal = result.first;
const int &numSuccessful = result.second;

std::cout << numSuccessful << " of " << numTotal << std::endl;

This freeing us from writing comments about semantics of first and second. What disadvantages of this way? Do compilers will reserve stack for numTotal and numSuccessful? Will perfomance drop if this pattern used in main loop of application?

Fixes

Changed from regular variables to reference variables (thank you for comments)

Answer

Jonathan Wakely picture Jonathan Wakely · Feb 7, 2014

I don't see any serious disadvantages, variables with meaningful names can help make the code much clearer. A decent optimizing compiler should be able to remove any overhead from the extra references in simple cases such as your example, but maybe not for more complex cases where the types are not identical (e.g. they have different const-qualification or require conversions).

There is another option that might be clearer in some cases: Instead of initializing a pair with the result, you could create the variables you want, then create a pair of references to those variables, and assign to them via the references:

int numTotal;
int NumSuccessful;
std::pair<int&, int&> result(numTotal, numSuccessful);
result = processSomething();

Or the same thing, without a named variable for the pair of references:

int numTotal;
int NumSuccessful;
std::pair<int&, int&>(numTotal, numSuccessful) = processSomething();

or in C++11 you can use the standard tie function:

int numTotal;
int NumSuccessful;
std::tie(numTotal, numSuccessful) = processSomething();

A more unusual solution involves no temporary objects and allows you to make the variables const, by using a local type with meaningful member names:

struct Num {
  Num(std::pair<const int, const int> p) : total(p.first), successful(p.second) { }
  int total;
  int sucessful;
};
const Num num = processSomething();
std::cout << num.successful << '/' << num.total << '\n';