3

This may be a duplicate, but I haven't found the problem anywhere else yet. Given the following code:

#include <functional>
#include <algorithm>
#include <iostream>
#include <vector>

template<typename container_ty_>
auto where(container_ty_ const& V, std::function<bool(typename container_ty_::value_type const&)>&& comp) 
-> std::vector<std::reference_wrapper<typename container_ty_::value_type>> {
        std::vector<std::reference_wrapper<typename container_ty_::value_type>> cursor;

        for(typename container_ty_::value_type const& VAL : V)
                if(comp(VAL))
                        cursor.push_back(const_cast<typename container_ty_::value_type&>(VAL));

        return cursor;
}

int main(int argc, char** argv) {
        std::vector<int> tVect = {0, 5, 2, 1, 7, 9};

        //Why must std::vector<int> be passed...
        auto vec = where<std::vector<int>>(tVect, [](const int& V) -> bool { return V > 5; });

        std::for_each(vec.begin(), vec.end(), [] (int& v) { std::cout << v++ << std::endl; });
        std::cout << std::endl;
        std::for_each(tVect.begin(), tVect.end(), [](int& v) { std::cout << v << std::endl; });
}

The line where vec is being assigned, the function where seems to need to have std::vector<int> passed into it in order to compile. if now i get:

testing.cpp:20:68: error: no matching function for call to ‘where(std::vector<int>&, main(int, char**)::__lambda0)’ auto vec = where(tVect, [](const int& V) -> bool { return V > 5; });

I suspect this is because the template is not being deduced correctly for the second argument of where could anyone explain to me why, i seem to be at a standstill...

Also: Command line args: g++ testing.cpp -g -o testing -std=c++11 -Wall

G++ version: g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4

  • Compiles in g++ 5.4 and 6.1 without the explicit type. – md5i Jun 29 '16 at 17:34
  • It compiles with GCC5, even if I don't specialize explicitly the template. What compiler are you using? – skypjack Jun 29 '16 at 17:36
  • @skypjack I'm using g++, it's in the question: `g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4` – Toppest.Of.Kek Jun 29 '16 at 17:37
  • @Toppest.Of.Kek oops, sorry, missed it. :-) – skypjack Jun 29 '16 at 17:37
  • Your second parameter is sketchy. It doesn't look like there's any reason to use the heavyweight `std::function` over a template parameter, and that `&&` disallows the user from passing an lvalue of that type. – chris Jun 29 '16 at 17:38
  • Some more data. Fails in 4.8, succeeds in 4.9. – md5i Jun 29 '16 at 17:43
  • @chris I changed the code to use a second template parameter and it worked fine, but it still makes me wonder why it won't work in 4.8 of g++ – Toppest.Of.Kek Jun 29 '16 at 17:45
  • It would appear to be a compiler issue, not a problem with your code. Compiles with `g++ (GCC) 5.3.1 20151207 (Red Hat 5.3.1-2)`, along with the versions mentioned by others. Compiles with clang, according to trygub below. Compiles with Visual Studio 2015 (build `19.00.23720.0 (x86)`). Even compiles with Visual Studio 2010 (which had kinda shoddy partial C++0x support), if you turn the range-based `for` into an equivalent normal `for` by manually assigning `VAL` inside the loop body, and fill the vector manually. – Justin Time - Reinstate Monica Jun 29 '16 at 17:51
  • out of interest, do you want the cursor to be writeable or readonly? – Richard Hodges Jun 29 '16 at 17:59

2 Answers2

1

You may be interested in a slightly more flexible version, which:

  1. preserves constness of the values in the source vector (depending on the constness of the vector itself)

  2. Takes any functor, no need for a std::function

-

#include <algorithm>
#include <iostream>
#include <vector>

template<typename container_ty_, class Comp>
auto where(container_ty_& V, Comp&& comp)
{
    using value_type = typename container_ty_::value_type;
    using reference =
    std::conditional_t<
      std::is_const<container_ty_>::value,
        std::reference_wrapper<const value_type>,
        std::reference_wrapper<value_type>
    >;

    std::vector<reference> cursor;

    for(auto& VAL : V)
        if(comp(VAL))
            cursor.push_back(VAL);

    return cursor;
}

int main(int argc, char** argv) {
    std::vector<int> tVect = {0, 5, 2, 1, 7, 9};

    //Why must std::vector<int> be passed...
    auto vec = where(tVect, [](const int& V) -> bool { return V > 5; });

    std::for_each(vec.begin(), vec.end(), [] (int& v) { std::cout << v++ << std::endl; });
    std::cout << std::endl;
    std::for_each(tVect.begin(), tVect.end(), [](const int& v) { std::cout << v << std::endl; });
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

If I do not provide the type parameter explicitly I confirm I can reproduce the problem with g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4.

This problem seems to be specific to gcc version 4.8. E.g., the code compiles with newer versions of gcc, and, on the same platform, clang++-3.6 compiles this code with the same command line parameters.

trygub
  • 87
  • 8