0

For a homework assignment I have to create a templatized standard deviation function that can be performed on any container. Here's what I have:

template <typename Container>
double findMean(Container c, int count){
    double sum = 0;
    for (auto&& e : c){
        sum += e;
    }
    sum /= count;
    return sum;
}

template <typename Container>
double findStDev(Container c){
    double mean = findMean(c, c.size());
    std::cout << mean << std::endl;
    for (auto&& e : c){
        e -= mean;
        e *= e;
    }
    mean = sqrt(findMean(c, c.size()));
    return mean;
}

The first time I find the mean I want to divide by the full size of the container (n), but when I find it the second time for the standard deviation, I need to divide by size-1 (n-1).

Is the .size() function available for all c++ containers?

Lancer521
  • 81
  • 3
  • 11

3 Answers3

8

Almost. By table 96 - container requirements in N3797, all containers in the standard library must provide a member function size. It shall have constant execution time and return the value of distance(a.begin(),a.end()) for a container a.

However, there is one (and only one) exception mentioned later on:

A forward_list satisfies all of the requirements of a container (Table 96), except that the size() member function is not provided.

(N3797 23.3.4.1 Clause 2)

That means that std::forward_list is indeed a standard container that does not have a member function size.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
2

In the good old tradition of the STL, you could have your template accept a pair of forward iterators and count the distance as it sums up the elements.

#include <cstddef>
#include <iterator>

template<typename FwdIter,
         typename value_type = typename std::iterator_traits<FwdIter>::value_type>
value_type
mean(const FwdIter begin, const FwdIter end)
{
  std::size_t count {0};
  value_type sum {};
  for (auto it = begin; it != end; ++it)
    {
      sum += *it;
      ++count;
    }
  return sum / count;
}

This will work for standard library containers, arrays, pointers, whatever you want. For containers, you can simply define a convenience forwarding template that calls cbegin and cend if you want.

Note that I have left out any type constraints from the above example. In practice, you should probably std::enable_if the template only under special conditions such as if std::is_arithmetic<value_type>.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • Also note that an often ignored advantage of this approach is that you can run your algorithm over ranges that are not a container, it could be a pair of *input iterators*, or it could be a subset of a container (say, mean of the highest 10% of the entries). +1 – David Rodríguez - dribeas Jan 12 '15 at 03:25
2

Do all containers have a .size() function?

No. Most of them do, and before C++11, all of them did. However, C++11 saw the introduction of the singly-linked list class template std::forward_list, which does not have a size() member function. All of the other containers do, and the current standard specifies that the algorithmic complexity is O(1). Pre-C++11, std::list was allowed to have linear complexity.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • `std::forward_list` does not fulfill the requirements of container (see N3797 23.3.4.1 Clause 2: "A forward_list satisfies all of the requirements of a container (Table 96), except that the size() member function is not provided."), so although it is described in paragraph 23 (Containers library), it is not a container in the standard sense. Is this wrong? – Baum mit Augen Jan 10 '15 at 23:46
  • @BaummitAugen It is a standard library sequence container ("A forward_list is a container that supports forward iterators ...") There is just an exception in a requirement (like there used to be for `std::list`.) – juanchopanza Jan 10 '15 at 23:49
  • Alright, thank you. Since I cannot delete my answer because it is accepted, I will correct it in an edit. – Baum mit Augen Jan 10 '15 at 23:54