Count the frequency of integers in an array I recently reviewed a question here on Code Review. The problem statement is


  Write a program that prompts the user to input ten values between 80 and 85 and stores them in an array. Your program must be able to count the frequency of each value appears in the array.


I coded my own solution for the problem statement. My questions are
  - Can this be optimized more?
  - Did I miss anything in C++14 or C++17 that might improve the code?
  - Is the code readable?
  - Are the variable and function names good or can they be improved?

My goals were to write the best C++ I could, remove any magic numbers and allow the solution to scale for different sets of numbers.

#include "pch.h"
#include &lt;iostream&gt;
#include &lt;array&gt;

const size_t INPUTSIZE = 10;
const size_t FREQUENCYSIZE = 6;
const int MINLEGALVALUE = 80;
const int MAXLEGALVALUE = 85;

std::array&lt;int, INPUTSIZE&gt; getUserInput()
{
    std::array&lt;int, INPUTSIZE&gt; inputValues;

    size_t i = 0;
    do
    {
        int inputValue = 0;
        std::cout &lt;&lt; "Please enter a number between " &lt;&lt; MINLEGALVALUE &lt;&lt; " and " &lt;&lt; MAXLEGALVALUE  &lt;&lt; ":" ;
        std::cin &gt;&gt; inputValue;

        if (inputValue &gt;= MINLEGALVALUE &amp;&amp; inputValue &lt;= MAXLEGALVALUE)
        {
            inputValues[i] = inputValue;
            i++;
        }
        else
        {
            std::cout &lt;&lt; "The number must be between" &lt;&lt; MINLEGALVALUE &lt;&lt; " and " &lt;&lt; MAXLEGALVALUE &lt;&lt; "\n";
        }

    } while (i &lt; INPUTSIZE);

    return inputValues;
}

std::array&lt;unsigned, FREQUENCYSIZE&gt; getFrequencyCounts(std::array&lt;int, INPUTSIZE&gt; inputValues)
{
    std::array&lt;unsigned, FREQUENCYSIZE&gt; freqs = { 0 };

    for (auto inputs : inputValues)
    {
        freqs[inputs - MINLEGALVALUE]++;
    }

    return freqs;
}

void printFrequencies(std::array&lt;unsigned, FREQUENCYSIZE&gt; freqs)
{
    unsigned rowLabel = MINLEGALVALUE;
    for (auto frequency : freqs)
    {
        std::cout &lt;&lt; rowLabel &lt;&lt; "      " &lt;&lt; frequency &lt;&lt; "\n";
        rowLabel++;
    }
}

int main()
{
    std::array&lt;int, INPUTSIZE&gt; inputValues = getUserInput();
    std::array&lt;unsigned, FREQUENCYSIZE&gt; freqs = getFrequencyCounts(inputValues);

    std::cout &lt;&lt; "\n";

    printFrequencies(freqs);
}
Names

All-caps names are typically reserved for macros. They don't seem to me to make much sense for const variables. In fact, they only make minimal sense for object-like macros--they were originally used for function-like macros as kind of a warning that you should be cautious about passing an argument with side-effects, because they might happen more than once.

Minimize Magic

I'd typically try to keep the magic numbers to a minimum. For example, instead of defining FREQUENCYSIZE by itself, I'd probably do something like this:

const int lower_bound = 80;
count int upper_bound = 85;
const int frequency_size = upper_bound - lower_bound + 1;


Separation of Concerns

I'd at least consider separating validating data from reading the data. I'd prefer to have a function on the general order of:

bool valid(int val) { 
    return val &gt;= lower_bound &amp;&amp; val &lt; upper_bound;
}


Class Usage

We have a number of different things related to reading and working with numbers in a specified range. It might be worth considering wrapping those bits and pieces into a coherent class for dealing with a value in a range, and let the outside world create and use objects of that class.

template &lt;class T, T lower_bound, T upper_bound&gt;
class bounded {
public:
    static bool valid(T val) { return val &gt;= lower_bound &amp;&amp; val &lt; upper_bound; }

    friend std::istream &amp;operator&gt;&gt;(std::istream &amp;is, bounded &amp;b) { 
        T val;
        is &gt;&gt; val;
        if (valid(val)) 
            b.val = val;
        else
            is.setstate(std::ios_base::failbit);
        return is;
    }

    friend std::ostream &amp;operator&lt;&lt;(std::ostream &amp;os, bounded const &amp;b) {
        return os &lt;&lt; b.val;
    }

    size_t index() { return size_t(val - lower_bound); }

    static constexpr size_t range() { return upper_bound - lower_bound + 1; }

private:
    T val;
};


That let's us simplify the rest of the code a bit, something on this general order:

int main() {
    using integer = bounded&lt;int, 80, 85&gt;;

    std::array&lt;integer, 10&gt; inputs;
    std::array&lt;size_t, integer::range()&gt; freqs {};

    for (integer &amp;i : inputs) {
        std::cin &gt;&gt; i;
        ++freqs[i.index()];
    }

    for (auto freq : freqs)
        std::cout &lt;&lt; freq &lt;&lt; "\n";
}


Technically, this doesn't meet the requirements as-is (e.g., it doesn't print out a prompt to tell the user to enter data), but I think it gives at least some idea of a direction things could go.