Swift - Friday Pie
Swift - Friday Pie

Reputation: 14673

Template deduction: porting to C++11

Following code is legal for C++14 compiler

// g++ -std=c++14 -pedantic -pthread main.cpp
// output: 1 2 3 4 5 1 1 1 
#include <algorithm>
#include <vector>
#include <functional>
#include <iterator>
#include <iostream>

int main()
{
  std::vector<int> a = { 1, 2, 3, 2, 4, 5, 1, 1, 3, 5, 1, 5 }, b = { 2, 5, 5, 3 }, c;

  std::copy_if(a.begin(), a.end(), std::back_inserter(c), 
    std::bind(std::less<>(),   // this won't work in pre-C++14
      std::bind(
        std::count<std::vector<int>::iterator, int>, 
          std::bind(static_cast<std::vector<int>::iterator (std::vector<int>::*)()>(&std::vector<int>::begin), &c), 
          std::bind(static_cast<std::vector<int>::iterator (std::vector<int>::*)()>(&std::vector<int>::end), &c), 
          std::placeholders::_1
      ),
      std::bind(
        std::minus<>(), // this won't work in pre-C++14
          std::bind(
            std::count<std::vector<int>::iterator, int>, 
              a.begin(), 
              a.end(), 
              std::placeholders::_1
          ),
          std::bind(
            std::count<std::vector<int>::iterator, int>, 
              b.begin(), 
              b.end(), 
              std::placeholders::_1
          )
      )
    )
  );

  std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;
}

It is meant to create vector c from elements of vector a , excluding those that match count and value of elements in vector b. E.g. if a contains three 2 and b - two of them, only one 2 would be present in c.

a) how possible to adapt this code for C++11? Intuitive leap that less<> and minus<> parameter would be ...::difference_type didn't work, compiler messages aren't helpful either

b) current version removes last matches sequence-wise. What code would remove first matches?

Upvotes: 2

Views: 124

Answers (1)

Barry
Barry

Reputation: 303517

The real answer is to just not use bind(). Use a lambda:

std::copy_if(a.begin(), a.end(), std::back_inserter(c), [&](int elem){
    return std::count(c.begin(), c.end(), elem) <
        std::count(a.begin(), a.end(), elem) - std::count(b.begin(), b.end(), elem);
});

This is far shorter than the solution with std::bind(), works in C++11, and is far easier to understand. And we don't have to do manual template deduction either.

We could have also written it this way:

std::copy_if(a.begin(), a.end(), std::back_inserter(c), [&](int& elem){
    return std::count(&elem + 1, &a[a.size()], elem) >=
        std::count(b.begin(), b.end(), elem);
});

Note that I'm now taking elem by reference. This also makes it more easier to see how to implement your proposed extension removing the first match. That's just changing which side of elem in a we're comparing:

std::copy_if(a.begin(), a.end(), std::back_inserter(c), [&](int& elem) {
    return std::count(&a[0], &elem, elem) >=
        std::count(b.begin(), b.end(), elem);
});

Upvotes: 7

Related Questions