4

I was trying to copy the values of a map to a vector using std::transform. Following this answer, I managed to to so. While searching for the way to do it, I learned about select2nd. So I tried to implement a "select2nd-like" operation using std::bind. However, when defining a select2nd alias, I get:

test.cpp:9:19: error: expected type-specifier
 using select2nd = std::bind(&T::value_type::second, std::placeholders::_1);
                   ^
test.cpp: In function ‘int main()’:
test.cpp:29:64: error: ‘select2nd’ was not declared in this scope
     std::transform(m.begin(), m.end(), std::back_inserter(v2), select2nd);

This is the snippet that I've come up with:

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

template <typename T>
using select2nd = std::bind(&T::value_type::second, std::placeholders::_1);

using std::placeholders::_1;

void printn(int i) {
    std::cout << i << " ";
}

int main() {
    std::map<int, int> m;
    std::vector<int> v1, v2;

    for (auto &i : { 0, 1, 2, 3, 4 }) {
        m[i] = rand();
    }

    // Works
    std::transform(m.begin(), m.end(), std::back_inserter(v1),
            std::bind(&std::map<int, int>::value_type::second, _1));
    // Doesn't
    std::transform(m.begin(), m.end(), std::back_inserter(v2), select2nd);

    std::for_each(v1.begin(), v1.end(), std::bind(printn, _1));
    std::cout << std::endl;
    std::for_each(v2.begin(), v2.end(), std::bind(printn, _1));
    std::cout << std::endl;

    return 0;
}

Why does the first transform work, but not the second? How can I parameterize an alias to std::bind?

Community
  • 1
  • 1
Rodrigo
  • 81
  • 5
  • 1
    `std::bind(whatever)` is a value, not a type. If you need its type, you can use `decltype(std::bind(whatever))`. OTOH `&T::value_type::second` is a type, not value. If you need a value, you can use `std::declval<&T::value_type::second>()`. – n. m. could be an AI Jul 20 '14 at 10:10
  • Nice catch! Hadn't noticed that I was binding a type to a value. Thanks! It still doesn't work, though. With `std::declval`, I get a `no matching function for call to 'declval'`. Without it, a `no matching constructor for initialization of 'select2nd>` – Rodrigo Jul 20 '14 at 10:36
  • `for (const auto& p : m) v.push_back(p.second);` – Felix Glas Jul 20 '14 at 16:36

1 Answers1

4

This should work, but not nicer:

template <typename T>
decltype(std::bind(&T::value_type::second, std::placeholders::_1)) select2nd(T m) {
    return std::bind(&T::value_type::second, std::placeholders::_1);
}

// ....

std::transform(m.begin(), m.end(), std::back_inserter(v2), select2nd(m));

// ....

Here is another alternative, without requiring an argument:

template <typename T>
    decltype(std::bind(&T::value_type::second, std::placeholders::_1)) select2nd() {
        return std::bind(&T::value_type::second, std::placeholders::_1);
    }

You can use this like:

std::transform(m.begin(), m.end(), std::back_inserter(v2), 
                                select2nd<std::map<int, int>>());

or:

std::transform(m.begin(), m.end(), std::back_inserter(v2), 
                                select2nd<decltype(m)>());
perreal
  • 94,503
  • 21
  • 155
  • 181