wyer33
wyer33

Reputation: 6350

Compiler error when passing a binary_function to inner_product inside another function

The following code:

$ cat test02.cpp
#include <string>
#include <numeric>
#include <cstdlib>
#include <list>
#include <iostream>

struct myadd :
    public std::binary_function
        <const std::string&,const std::string&,std::string>
{
    std::string operator () (const std::string& x,const std::string& y) const {
        return x+" "+y;
    }
};

struct mymul :
    public std::binary_function
        <const std::string&,const std::string&,std::string>
{
    std::string operator () (const std::string& x,const std::string& y) const {
        return x+y;
    }
};

std::string spliceme(
    const std::list <std::string>& list1,
    const std::list <std::string>& list2,
    const std::binary_function
        <const std::string&,const std::string&,std::string>& add,
    const std::binary_function
        <const std::string&,const std::string&,std::string>& mul
) {
    return std::inner_product(list1.cbegin(),list1.cend(),list2.cbegin(),
        std::string(""),add,mul);
}

int main() {
    std::list <std::string> list1;
        list1.emplace_back("First");
        list1.emplace_back("Second");
        list1.emplace_back("Third");
    std::list <std::string> list2;
        list2.emplace_back("Foerst");
        list2.emplace_back("Annen");
        list2.emplace_back("Tredje");

    std::string result = spliceme(list1,list2,myadd(),mymul());
    std::cout << result << std::endl;

    return EXIT_SUCCESS;
}

Generates the compiler error:

g++ -std=c++0x test02.cpp -o test02
In file included from /usr/lib/gcc/i686-pc-linux-gnu/4.6.3/include/g++-v4/numeric:62:0,
                 from test02.cpp:2:
/usr/lib/gcc/i686-pc-linux-gnu/4.6.3/include/g++-v4/bits/stl_numeric.h: In function '_Tp std::inner_product(_InputIterator1, _InputIterator1, _InputIterator2, _Tp, _BinaryOperation1, _BinaryOperation2) [with _InputIterator1 = std::_List_const_iterator<std::basic_string<char> >, _InputIterator2 = std::_List_const_iterator<std::basic_string<char> >, _Tp = std::basic_string<char>, _BinaryOperation1 = myadd, _BinaryOperation2 = std::binary_function<const std::basic_string<char>&, const std::basic_string<char>&, std::basic_string<char> >]':
test02.cpp:29:101:   instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.6.3/include/g++-v4/bits/stl_numeric.h:218:2: error: no match for call to '(std::binary_function<const std::basic_string<char>&, const std::basic_string<char>&, std::basic_string<char> >) (const std::basic_string<char>&, const std::basic_string<char>&)'
make: *** [all] Error 1

The issue occurs on the line:

return std::inner_product(list1.cbegin(),list1.cend(),list2.cbegin(),
    std::string(""),add,mul);

If I instantiate the classes myadd and mymul directly:

return std::inner_product(list1.cbegin(),list1.cend(),list2.cbegin(),
    std::string(""),myadd(),mymul());

Everything compiles and runs correctly. What's wrong with the way I'm passing in the functions add and mul into the function spliceme?

Upvotes: 0

Views: 349

Answers (3)

Nevin
Nevin

Reputation: 4863

std::binary_function was a convenience template for adding three typedefs to your function object. It was never intended to be used as a base class in function signatures, because all that happens is your object gets sliced. It has been deprecated in C++11 and should no longer be used. Yakk gives a great answer on what you should be doing. If you really need those typedefs (first_argument_type, second_argument_type and result_type), add them yourself.

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

Either this:

std::string spliceme(
  const std::list <std::string>& list1,
  const std::list <std::string>& list2,
  const std::function<std::string(const std::string&,const std::string&)>& add,
  const std::function<std::string(const std::string&,const std::string&)>& mul
) {
  return std::inner_product(list1.cbegin(),list1.cend(),list2.cbegin(),
    std::string(""),add,mul);
}

or this:

template<typename Add, typename Mul>
std::string spliceme(
  const std::list <std::string>& list1,
  const std::list <std::string>& list2,
  const Add& add,
  const Mul& mul
) {
  return std::inner_product(list1.cbegin(),list1.cend(),list2.cbegin(),
    std::string(""),add,mul);
}

makes your code work. The first uses type erased functions, the second uses template functors. The first lets you split your body out of the header, the second allows for improved inlineing.

Upvotes: 3

camino
camino

Reputation: 10594

std::string spliceme(
    const std::list <std::string>& list1,
    const std::list <std::string>& list2,
    const std::binary_function
        <const std::string&,const std::string&,std::string>& add,
    const std::binary_function
        <const std::string&,const std::string&,std::string>& mul
) 

should be:

std::string spliceme(
    const std::list <std::string>& list1,
    const std::list <std::string>& list2,
    const myadd& add,
    const mymul& mul
) {

because operator() is not virtual function

Upvotes: 2

Related Questions