Exercise: Reading doubles from a file and printing mean, max and min (from Programming Principles and Practice using C++) Read a sequence of double values into  a vector. Think of each value as the distance between two cities along a given route.  Compute and print the total distance (the sum of all distances). Find and print the smallest and greatest distance  between two neighboring cities. Find and print the mean distance between two  neighboring cities.


from Programming Principles and Practice using C++ by Bjarne Stroustrup

Since always entering values seems stupid, I decided to read the values from a file named "distances.txt".

print_distances_mean_max_min is what concerns me, how could I split the computing of min,max,mean and printing it while not spreading the code too thinly? Or is it fine as it is?

main.cpp

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;fstream&gt;
#include &lt;stdexcept&gt;
#include &lt;algorithm&gt;

using std::vector;
using std::string;
using std::fstream;
using std::ios_base;
using std::runtime_error;
using std::cout;
using std::max_element;
using std::min_element;

const string distances_file_name = "distances.txt";

vector&lt;double&gt; load_file_of_doubles_to_vector ( string file_name )
{
    vector&lt;double&gt; distances;
    fstream my_file ( file_name, ios_base::in );
    double temporary_number;
    while ( my_file &gt;&gt; temporary_number )
    {
        distances.push_back ( temporary_number );
    }
    if ( !my_file.is_open() )
    {
        throw runtime_error ( string ( "Failed to load " ) + file_name );
    }
    return distances;
}

void print_distances_mean_max_min ( const vector&lt;double&gt;&amp; distances_vector )
{
    if ( distances_vector.empty() )
    {
        throw runtime_error ( string ( "There are no entries in " ) + distances_file_name );
    }
    double total_distance = 0;
    for ( auto&amp; n : distances_vector )
    {
        total_distance += n;
    }
    cout &lt;&lt; "The sum of all distances is: " &lt;&lt; total_distance &lt;&lt; "\n";
    double max_distance = 0;
    max_distance = *max_element ( distances_vector.begin(), distances_vector.end() );
    cout &lt;&lt; "The maximum distance is: " &lt;&lt; max_distance &lt;&lt; "\n";
    double min_distance = 0;
    min_distance = *min_element ( distances_vector.begin(), distances_vector.end() );
    cout &lt;&lt; "The minimum distance is: " &lt;&lt; min_distance &lt;&lt; "\n";
    double mean_distance = 0;
    mean_distance = total_distance / distances_vector.size();
    cout &lt;&lt; "The average(mean) distance is: " &lt;&lt; mean_distance &lt;&lt; "\n";
}

int main()
{
    print_distances_mean_max_min ( load_file_of_doubles_to_vector ( distances_file_name ) );
    return 0;
}


distances.txt

8.7
6.3
9.0
3.142
2.7
Here are some suggestions for you to improve your code.




#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;fstream&gt;
#include &lt;stdexcept&gt;
#include &lt;algorithm&gt;



Sort the #include directives in alphabetical order.  This helps navigation.




using std::vector;
using std::string;
using std::fstream;
using std::ios_base;
using std::runtime_error;
using std::cout;
using std::max_element;
using std::min_element;



This is much better than using namespace std;, but I still suggest getting into the habit of explicitly qualifying the names with std:: as soon as possible (especially for those names which you only use once, e.g., max_element).  For small programs like this it doesn't matter, but for larger programs, qualification adds clarify &mdash; when I see string, does it mean std::string?  Jewelry::string, a string of pearls?  Or Minecraft::string, used to craft bows and fishing rods?  This problem is especially evident for common identifiers, like count, arg, or data &mdash; these are all top level names in std!

Moreover, using unqualified names sometimes cause ADL (argument dependent lookup), an additional phase for name lookup, to kick in.  As a result, an attempt to call a function in the standard library may end up inadvertently calling a function in an irrelevant namespace.  Such problems come up often in large programs, so again, it's better to get into the habit of using std::.

It is acceptable to use using declarations or derivatives in reduced scopes, to reduce clutter.  But using them globally for the whole program is likely to cause problems.  Moreover, std:: is only 5 characters &mdash; reducing clutter is more relevant for things like

boost::math::double_constants::pi


or 

boost::multiprecision::number&lt;
    boost::multiprecision::mpfr_float_backend&lt;300&gt;,
    boost::multiprecision::et_off
&gt;;





const string distances_file_name = "distances.txt";



Use constexpr std::string_view (defined in header &lt;string_view&gt;) for string constants:

constexpr std::string_view file_name{"distances.txt"};


or

using namespace std::literals;
constexpr auto file_name = "distances.txt"sv;





vector&lt;double&gt; load_file_of_doubles_to_vector ( string file_name )
{
    vector&lt;double&gt; distances;
    fstream my_file ( file_name, ios_base::in );
    double temporary_number;
    while ( my_file &gt;&gt; temporary_number )
    {
        distances.push_back ( temporary_number );
    }
    if ( !my_file.is_open() )
    {
        throw runtime_error ( string ( "Failed to load " ) + file_name );
    }
    return distances;
}



The parameter type should be std::string_view to avoid redundant copies.

The file stream should be of type std::ifstream instead of std::fstream because it is used for input only.  Then, you can omit the in option, which is the default for std::ifstream.

string ( "Failed to load " ) + file_name is too verbose.  Use "Failed to load "s.

Consider using std::istream_iterator, std::back_inserter (both defined in &lt;iterator&gt;), and std::copy to read the values:

// requires NTBS as argument
auto load_file(std::string_view file_name)
{
    // std::string_view doesn't have c_str()
    std::ifstream in{file_name.data()};
    if (!in.is_open()) {
        // assuming 'using namespace std::literals;'
        throw std::runtime_error{"the file doesn't exist"};
    }

    std::vector&lt;double&gt; data;
    std::copy(std::istream_iterator&lt;double&gt;{in},
              std::istream_iterator&lt;double&gt;{},
              std::back_inserter(data));
    return data;
}





void print_distances_mean_max_min ( const vector&lt;double&gt;&amp; distances_vector )
{
    if ( distances_vector.empty() )
    {
        throw runtime_error ( string ( "There are no entries in " ) + distances_file_name );
    }
    double total_distance = 0;
    for ( auto&amp; n : distances_vector )
    {
        total_distance += n;
    }
    cout &lt;&lt; "The sum of all distances is: " &lt;&lt; total_distance &lt;&lt; "\n";
    double max_distance = 0;
    max_distance = *max_element ( distances_vector.begin(), distances_vector.end() );
    cout &lt;&lt; "The maximum distance is: " &lt;&lt; max_distance &lt;&lt; "\n";
    double min_distance = 0;
    min_distance = *min_element ( distances_vector.begin(), distances_vector.end() );
    cout &lt;&lt; "The minimum distance is: " &lt;&lt; min_distance &lt;&lt; "\n";
    double mean_distance = 0;
    mean_distance = total_distance / distances_vector.size();
    cout &lt;&lt; "The average(mean) distance is: " &lt;&lt; mean_distance &lt;&lt; "\n";
}



IMO, this function is unnecessary and can be built into main directly, because it doesn't have a coherent logic &mdash; it just shows what the program does.

Catching exceptions in the main function and printing the messages helps the end user know what kind of error happened.

Don't reinvent the wheel &mdash; use std::accumulate or std::reduce (defined in header &lt;numeric&gt;) to calculate sums.

You can calculate the minimum and maximum values simultaneously in
\$\lfloor 3(N-1)/2 \rfloor\$ comparisons using std::minmax_element, which returns a std::pair of iterators pointing to the minimum and maximum elements respectively.  Then, we can use a structured binding to conveniently obtain the two iterators and dereference them to get the values:

int main()
try {
    auto data = load_file(file_name);
    if (data.empty()) {
        throw std::runtime_error{"no entries"};
    }

    auto sum = std::accumulate(data.begin(), data.end(), 0.0);
    auto mean = sum / data.size();
    auto [min_it, max_it] = std::minmax_element(data.begin(), data.end());

    std::cout &lt;&lt; "The sum of all distances is: " &lt;&lt; sum &lt;&lt; '\n'
              &lt;&lt; "The maximum distance is: " &lt;&lt; *max_it &lt;&lt; '\n'
              &lt;&lt; "The minimum distance is: " &lt;&lt; *min_it &lt;&lt; '\n'
              &lt;&lt; "The average (mean) distance is: " &lt;&lt; mean &lt;&lt; '\n';
} catch (std::runtime_error&amp; error) {
    std::cerr &lt;&lt; "Runtime error: " &lt;&lt; error.what() &lt;&lt; '\n';
    return 1;
} catch (...) {
    std::cerr &lt;&lt; "Unknown error\n";
    return 2;
}




I've made an online demo by putting everything together so that you can play with the code.