2

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?

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • 4
    oh wow, `std::bind`. Please don't use it (in both c++11 and c++14) – Ap31 Mar 29 '17 at 06:41
  • 1
    instead use a lambda, but I'd say you need to rethink your algorithms as well – Ap31 Mar 29 '17 at 06:48
  • @Ap31 that kind of offtopic comment to a theoretical question. Oh, I know there is notion: there are now lambdas so we don't need bind. But actually lambdas and bind predate C++11 but entered standard together and latter never existed without former (due implementation). The question wasn't to refactor code – Swift - Friday Pie Mar 29 '17 at 06:48
  • @Ap31 Why not use `std::bind`? – The Quantum Physicist Mar 29 '17 at 06:50
  • @Swift I agree that it could be offtopic (and sorry for that), but I believe that this is precisely the way to solve your problem. With labmda, you won't need the `std::minus<>` or `std::less<>`. Also the code will be much clearer to read – Ap31 Mar 29 '17 at 07:10
  • @Ap31 that's obvious that it can be replaced with copy_if(std::copy_if(a.begin(), a.end(), std::back_inserter(c), [=](int) { .... } ); in case of simple excluding of matching elements, less obvious solution (or actually less pretty than simple lambda) for counting repeats, but problem of manually deducing template parameters for backporting still exist, in various cases. less<>\minus<> in C++14 got implementation in which operator() itself is a template, that's why code above works, I think. This is an artificial example that replaces code I can't provide here – Swift - Friday Pie Mar 29 '17 at 07:21
  • @Swift I agree, `less<>`&`minus<>` have a lot of features. Features that are there to make them behave like raw `<` and `-` tokens in your code. With lambda you're able to just use the `<` and `-`, so why not do that? [here](https://ideone.com/IyR5yO), how is it not better? and it works perfectly with c++11 – Ap31 Mar 29 '17 at 07:34
  • 1
    Now for the real offtopic, [here](https://ideone.com/EAPvS7) is how I'd implement that. Of course, if you don't need to preserve the order, then perhaps there is a better way still – Ap31 Mar 29 '17 at 07:40
  • The lambda function is the prettier solution, but if you do not want to rewrite your code, you can just use explicit instantiations `std::less` and `std::minus`. Tested with Clang 3.8 and GCC 4.9.1. – Henri Menke Mar 29 '17 at 07:42
  • @Ap31 actual problem I have is featuring bind(proprietary_function , bind (less<>, bind(proprietary_function ... ), bind(proprietary_function ... ) but not in _one_ single line of code, but rather distributed among rather voluptuous template code. Re-implementing that would cause IP breach, refactoring is not going through the code check due "reasons", instead got stuck with a newer compiler that got QOI issues. Actually GCC with C++11 works with code above if I give instead of <>, but Microsoft compiler doesn't. – Swift - Friday Pie Mar 29 '17 at 07:43
  • @Henri Menke yeah, that works with GCC 4.9.. for some reason doesn't work with VC or with a proprietary compiler based off GCC 4.7 – Swift - Friday Pie Mar 29 '17 at 07:43
  • 1
    @Ap31: `std::set_difference` might be used if original order is not required. – Jarod42 Mar 29 '17 at 07:59
  • @Swift OK, so you're saying that while the world is preparing to transfer to C++17, you're stuck with a huge ugly codebase that you cannot refactor even a bit and instead you have to transfer it BACKWARDS to C++11 under the compilers that don't really work? – Ap31 Mar 29 '17 at 07:59
  • @Jarod yeah, exactly. I've said "perhaps" because I'm not sure if the sorting is worth it – Ap31 Mar 29 '17 at 08:00
  • @Ap31 First, problem is that C++17\C+14 implementation for platform still not stable. That's very sound assumption that world prepares to transfer to C++17. Even now C++11 isn't implemented everywhere. About 5/6 of Earth population lives in counties that have C++03 or incomplete C++11 as more commonly used platform. Also, that's embedded world for you, where you should be restrained in going "forward" especially, if target CPU exist in amount of instances described in 4-5 digit number. – Swift - Friday Pie Mar 29 '17 at 08:29
  • `minus<>` or `less<>` are trivially implemented in C++11. There's nothing in them that uses a C++14 feature. – T.C. Mar 29 '17 at 09:29
  • @T.C. you can't write minus<> in c++11, you shoul write minus – Swift - Friday Pie Mar 29 '17 at 09:41
  • 1
    Clearly the point went flying over your head, so I'll try again: it is trivial to write a type in C++11 that has the same functionality as C++14 `minus<>` in about five lines of code. If you are having so much problems, just implement them yourself. – T.C. Mar 29 '17 at 09:55
  • @T.C. yes, that is last resort that is very un-loved by code checkers. for one, it turns into heavy trail of platform-dependant header-files, with obscure(and not always working) preprocessor directives to select proper variant, or just re-implementing standard library – Swift - Friday Pie Mar 29 '17 at 12:03

1 Answers1

7

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);
});
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Why `&a[0]`? Have [data](http://en.cppreference.com/w/cpp/container/vector/data) issues? – Yakk - Adam Nevraumont Mar 29 '17 at 13:08
  • @Yakk Shorter and more symmetrical with the other version. – Barry Mar 29 '17 at 13:10
  • yet my problem is that i cant ditch at least one upmost bind and lcc(distributed sparc compiler) and compatible with it vc 2010 compiler does not accept less(), which works with fully c++11 compiler, neiither trivial implementation that would replace less<> does work – Swift - Friday Pie Mar 29 '17 at 15:39
  • @Swift Huh? I have no idea what that meant. – Barry Mar 29 '17 at 15:57
  • voted up for alternate lambda version i didn't thought of. I suppose, the easiest part to solve second part is to get container that support front push... e.g. std::list. the question formulates so it represent real-life problem that utterly cant be solved by closure use, because if some code unavailable, some is not portable, and target platforms include a spark system , a x64 linux (where everything wirks atm) and win7 embedded with vc2010 – Swift - Friday Pie Mar 29 '17 at 16:05
  • with new compiler this works faster on SPARC than bind version. Real case required refactoring of project though. – Swift - Friday Pie Aug 16 '17 at 07:45