Thread: bind2nd problems

  1. #1
    Registered User
    Join Date
    Oct 2005
    Posts
    271

    bind2nd problems

    I'm trying to implement a standard deviation routine using templates but I hit a snag with the bind2nd operator and I just don't get it:

    Code:
    template<typename T> struct minus_squared
        : public std::binary_function<T, T, T> {
        T operator()(const T& t1, const T& t2) const {
          return pow(t1 - t2, 2);
        }
      };
    
      template<typename S, template <typename T> class Container>
        double sd(const Container<S>& ct) {
        double m = mean(ct);
        return sqrt(std::accumulate(ct.begin(), ct.end(), 0.0,
                                    std::bind2nd(minus_squared<S>(), m))
                    / (ct.size() - 1));
      }
    I'm calling it with a vector of doubles and the error I get is:
    Code:
    /usr/include/c++/4.0.0/bits/stl_numeric.h: In function ‘_Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator<const double*, std::vector<double, std::allocator<double> > >, _Tp = double, _BinaryOperation = std::binder2nd<tlin::minus_squared<double> >]’:
    named_matrix.h:145:   instantiated from ‘double tlin::sd(const Container<S>&) [with S = double, Container = std::vector]’
    schone.cpp:14:   instantiated from here
    /usr/include/c++/4.0.0/bits/stl_numeric.h:116: error: no match for call to ‘(std::binder2nd<tlin::minus_squared<double> >) (double&, const double&)’
    /usr/include/c++/4.0.0/bits/stl_function.h:439: note: candidates are: typename _Operation::result_type std::binder2nd<_Operation>::operator()(const typename _Operation::first_argument_type&) const [with _Operation = tlin::minus_squared<double>]
    /usr/include/c++/4.0.0/bits/stl_function.h:445: note:                 typename _Operation::result_type std::binder2nd<_Operation>::operator()(typename _Operation::first_argument_type&) const [with _Operation = tlin::minus_squared<double>]
    meaning obviously that "binder2nd" is expecting only one argument but is getting two arguments. What I don't understand is why?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I think the problem is that std::accumulate() is supposed to use the function object provided to "sum" the elements. What you are trying to do is to apply the function object on each element, and then accumulate the result.

    One possibility is to use std::inner_product with a temporary vector, e.g.,
    Code:
    template<typename S, template <typename T> class Container>
    double sd(const Container<S>& ct) {
      std::vector<S> mean_values(ct.size(), mean(ct));
      return sqrt(std::inner_product(ct.begin(), ct.end(), mean_values.begin(),
        0.0, std::plus<S>(), minus_squared<S>()) / (ct.size() - 1));
      }
    But perhaps there is a more efficient way using the generic algorithms that avoids changing the original container without creating a temporary container.

    EDIT:
    Okay, this might work as a more efficient solution:
    Code:
    template<typename T> struct add_mean_diff
        : public std::binary_function<T, T, T> {
      add_mean_diff(double mean) : mean(mean) {}
    
      T operator()(const T& t1, const T& t2) const {
        T diff = t2 - mean;
        return t1 + (diff * diff);
        }
      private:
        T mean;
      };
    
    template<typename S, template <typename T> class Container>
    double sd(const Container<S>& ct) {
      return sqrt(std::accumulate(ct.begin(), ct.end(), 0.0,
        add_mean_diff<S>(mean(ct))) / (ct.size() - 1));
      }
    Last edited by laserlight; 02-06-2009 at 03:21 AM. Reason: Oops, you cannot actually ditch minus_squared because I, uh, replaced it with minus instead :o
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Code:
    template<typename S, template <typename T> class Container> double sd(const Container<S>& ct)
    This is almost as portable as successfully dereferencing null.

    The standard containers may have more template parameters than is strictly required.

    I don't think this particular attempt will work with any container. (Originally I had written "This will never work.".)

    If you actually want to support any container of any numeric type you'll either have to overload on the contained type with all the standard containers, have an infinite number of generic overloads adding template parameters, or just do it right and use the iterators, traits, and algorithms provided by the STL.

    Edit: I stole LL's 'add_mean_diff'--which is a truly crappy name--to write a good example.

    Soma

    Code:
    #include <algorithm>
    #include <cmath>
    #include <functional>
    #include <iostream>
    #include <numeric>
    
    template
    <
       typename ForwardIterator
    >
    typename std::iterator_traits<ForwardIterator>::value_type mean
    (
       const ForwardIterator first,
       const ForwardIterator last
    )
    {
       typedef std::iterator_traits<ForwardIterator> my_iterator_traits;
       typedef typename my_iterator_traits::value_type value_type;
       typedef typename my_iterator_traits::difference_type difference_type;
       const value_type sum_of_elements(std::accumulate(first, last, value_type()));
       const difference_type number_of_elements(std::distance(first, last));
       return(sum_of_elements / number_of_elements);
    }
    
    template
    <
       typename T
    >
    struct add_mean_diff: std::binary_function<T, T, T>
    {
       add_mean_diff
       (
          const T & f
       ):
          m(f)
       {
       }
       T operator ()
       (
          const T & p1,
          const T & p2
       ) const
       {
          T difference(p2 - m);
          return(p1 + (difference * difference));
       }
       T m;
    };
    
    template
    <
       typename ForwardIterator
    >
    typename std::iterator_traits<ForwardIterator>::value_type standard_deviation
    (
       const ForwardIterator first,
       const ForwardIterator last
    )
    {
       using std::sqrt;
       typedef std::iterator_traits<ForwardIterator> my_iterator_traits;
       typedef typename my_iterator_traits::value_type value_type;
       typedef typename my_iterator_traits::difference_type difference_type;
       const value_type mean_of_elements(mean(first, last));
       const difference_type number_of_elements(std::distance(first, last));
       return(sqrt(std::accumulate(first, last, value_type(), add_mean_diff<value_type>(mean_of_elements)) / number_of_elements));
    }
    
    int main()
    {
       double values[] = {34, 27, 45, 55, 22, 34};
       std::cout << standard_deviation(values, values + (sizeof(values) / sizeof(*values))) << '\n';
       return(0);
    }

  4. #4
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by phantomotap View Post
    Code:
    template<typename S, template <typename T> class Container> double sd(const Container<S>& ct)
    This is almost as portable as successfully dereferencing null.
    And by that I assume you simply mean that VC6 might not handle it?
    It looks perfectly sane to me though, and I've used that kind of template expression a number of times.

    If you're going to take a dig at 'add_mean_diff' being a crappy name, then why couldn't you think of anything better!
    Last edited by iMalc; 02-06-2009 at 01:08 PM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  5. #5
    The larch
    Join Date
    May 2006
    Posts
    3,573
    And by that I assume you simply mean that VC6 might not handle it?
    o_O

    I think he means that this kind of stuff is not generic enough ... by C++ standards. Using Iterators as template parameters is indeed a lot more flexible.

    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    And by that I assume you simply mean that VC6 might not handle it?
    One parameter to a container? No, that's just plain invalid. Every standard container has at least two parameters (data type and allocator) and the standard allows even more, as long as they have defaults. And defaults are not considered when matching templates to template template arguments.
    In other words, this cannot work with any standard container, unless the compiler has a special extension for considering defaults.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User
    Join Date
    Oct 2005
    Posts
    271
    Quote Originally Posted by laserlight View Post
    I think the problem is that std::accumulate() is supposed to use the function object provided to "sum" the elements. What you are trying to do is to apply the function object on each element, and then accumulate the result.
    Thanks. I had misunderstood how accumulate functions. Now I know better.

  8. #8
    Registered User
    Join Date
    Oct 2005
    Posts
    271
    Quote Originally Posted by CornedBee View Post
    One parameter to a container? No, that's just plain invalid. Every standard container has at least two parameters (data type and allocator) and the standard allows even more, as long as they have defaults. And defaults are not considered when matching templates to template template arguments.
    In other words, this cannot work with any standard container, unless the compiler has a special extension for considering defaults.
    I discovered this just now. My compiled fine on my Mac with gcc 4.0, but when I tried to compile it on a Linux machine with gcc 4.2.2, it popped an error message. I'll just write code for standard deviation in plain, unvarnished code.

  9. #9
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I think he means that this kind of stuff [...] by C++ standards.
    Considering it doesn't even work with the standard containers, it absolutely isn't generic enough.

    It looks perfectly [...] a number of times.
    Who cares? I've successfully dereferenced null. That doesn't make it standard or portable.

    If you're going to [...] anything better!
    I wouldn't have given it a name at all as it doesn't deserve one. I would have used my lambda utilities to write it directly inside the function. That wouldn't have been very useful for the OP though would it?

    I'm curious, did you have a good reason to take a dig at me? Or was it just that you think I'm was wrong?

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. No clue how to make a code to solve problems!
    By ctnzn in forum C Programming
    Replies: 8
    Last Post: 10-16-2008, 02:59 AM
  2. String Manipulation problems -_-
    By Astra in forum C Programming
    Replies: 5
    Last Post: 12-13-2006, 05:48 PM
  3. Rendering problems (DirectX?)
    By OnionKnight in forum Tech Board
    Replies: 0
    Last Post: 08-17-2006, 12:17 PM
  4. contest problems on my site
    By DavidP in forum Contests Board
    Replies: 4
    Last Post: 01-10-2004, 09:19 PM
  5. DJGPP problems
    By stormswift in forum C Programming
    Replies: 2
    Last Post: 02-26-2002, 04:35 PM