1

I would like to initialize a vector of pair<char, int> in one line using STL. From what I have found the following works but can only initialize every element with the same value.

vector<pair<char, int>> myVec (26, std::make_pair('a', -1));

Is there a way to initialize it, maybe with a lamda function or something, so that the char elements will be incremental like:

(a,-1) (b,-1) (c,-1) etc...

and all this in one line without using a classic loop after initialization? Thanks in advance.

BugShotGG
  • 5,008
  • 8
  • 47
  • 63
  • 1
    The `std::vector` constructor does not support this, use something like `std::iota` – Hatted Rooster Jun 27 '16 at 10:22
  • Take a look at this question: http://stackoverflow.com/q/12511711/5513245 and specifically the first two answers. They could possibly help you accomplish what you want to do! – Jonathan Jun 27 '16 at 10:23
  • Not without some boilerplate or a fairly complex lambda. – Richard Hodges Jun 27 '16 at 10:24
  • Do you intend the first of each pair to be a letter? The reason I ask is that there are real-world character sets in which the letters a-z are not contiguous (e.g. there are characters between `a` and `z` which are not letters). – Peter Jun 27 '16 at 10:31
  • @RichardHodges I would really love to see that solution, just for educational purposes. – BugShotGG Jun 27 '16 at 10:32
  • @BugshotGG answer posted – Richard Hodges Jun 27 '16 at 10:32
  • @Peter Well no, it can be whatever, if it suits you for `` the question is still the same :) – BugShotGG Jun 27 '16 at 10:33
  • If you can use boost, then maybe you could pass a `boost::zip_iterator` to your constructor which zips the sequence of letters with the (infinite) repetition of -1. – Frerich Raabe Jun 27 '16 at 10:53
  • No worries, BugShotGG. It's just that the answers would differ if you wanted sequential letters, rather than simply incrementing the numeric value. – Peter Jun 27 '16 at 11:53

6 Answers6

6

This is not possible (as one-liner) without using some kind of boilerplate or external dependency (boost for example, see other answers)

A two-liner using std::generate:

std::vector<std::pair<char, int>> v(26);
std::generate(v.begin(), v.end(), [] { static char c = 'a'; return std::make_pair(c++, -1);} );
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
2

This is not quite one line, but here is an approach that amounts to calling push_back without loops:

vector<pair<char,int>> v;
generate_n(
    back_insert_iterator<std::vector<pair<char,int>>>(v)
,   26
,   [c = 'a']() mutable { return make_pair(c++, -1); }
);

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

Or you could use std::generate or std::generate_n.

Or hand-written range-based for loop — I'd personally prefer this approach.


Or you can use BOOST_PP_ENUM as:

#include <iostream>
#include <boost/preprocessor/repetition/enum.hpp>

#define PAIR(count, i, data) std::make_pair('a' + i, -1)

int main() 
{
  std::vector<std::pair<char, int>> vec { BOOST_PP_ENUM(26, PAIR, ~) };
}

The line:

std::vector<std::pair<char, int>> vec { BOOST_PP_ENUM(26, PAIR, ~) };

expands to this:

std::vector<std::pair<char, int>> vec {
      std::make_pair('a' + 0, -1),
      std::make_pair('a' + 1, -1),
      std::make_pair('a' + 2, -1),
      .
      .
      std::make_pair('a' + 25, -1)
};
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • one might ask, if we're going to establish some macro boilerplate, why not some code boilerplate? – Richard Hodges Jun 27 '16 at 10:24
  • I'd personally prefer hand-written *range-based* `for` loop. – Nawaz Jun 27 '16 at 10:26
  • I'm not sure I like this suggestion. The macros are generally less safe than just writing the code for it. Also, the OP appears to be looking for something that relies on the STL, and referencing boost adds a dependency. While we don't know the OP's take on dependencies, it seems overkill linking boost just to do this. Using a loop would've been more appropriate, IMHO. – code_dredd Jun 27 '16 at 10:28
  • @ray: I agree. there're lots of ways to fill the array. as to which is best depends on the actual scenario. – Nawaz Jun 27 '16 at 10:29
0

Whichever way you cut it, you're going to have to define the concept of what "vector generation" means.

In the end, probably the cleanest way to express this is to encapsulate the vector generation in a function, and then rely on RVO to "do the right thing".

You'll find that it's also nice to be able to place breakpoints when debugging.

#include <vector>
#include <utility>

using namespace std;

// define the concept of 'a generated vector'.
// I used a lambda but this could just as well be a free function
// or a function object
auto generate_vec = [] (std::size_t n, char first_char) {
  vector<pair<char, int>> myVec;
  myVec.reserve(n);
  while (n--) {
    myVec.emplace_back(first_char++, -1);
  }
  return myVec;
};

int main()
{
    // use the concept here
    vector<pair<char, int>> myVec  = generate_vec(26, 'a');
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

With some template overhead written once:

#include <utility>

// Make Sequence
// =============

namespace Detail {
    template <typename ResultSequence, typename IntegerSequence>
    struct make_sequence;

    template <typename ResultSequence, typename IndexType, IndexType...Indices>
    struct make_sequence<ResultSequence, std::integer_sequence<IndexType, Indices...>>
    {
        template <typename Callable, typename...Args>
        static constexpr ResultSequence apply(Callable&& fn, Args&&...args)
        {
            return { fn(Indices, std::forward<Args>(args)...) ... };
        }
    };
} // namespace Detail

template <typename ResultSequence, std::size_t N, typename Callable, typename...Args>
constexpr ResultSequence make_sequence(Callable&& fn, Args&&...args)
{
    return Detail::make_sequence<ResultSequence, std::make_index_sequence<N>>::apply(fn, args...);
}

// Test
// ====

#include <array>
#include <iostream>
#include <vector>
#include <map>
int main() {
    struct MissingConstexprLambda
    {
        constexpr std::pair<char, int> operator () (std::size_t i) {
            return std::make_pair(char('a' + i), -1);
        };
    };

    std::cout << "Array:\n";
    constexpr auto a = make_sequence<std::array<std::pair<char, int>, 3>, 3>(MissingConstexprLambda());
    for(const auto& p : a)
        std::cout << p.first << " = " << p.second << '\n';
    static_assert(std::get<1>(a).first == 'b', "");

    auto lambda = [](std::size_t i) { return std::make_pair('a' + i, -1); };

    std::cout << "Vector:\n";
    auto v = make_sequence<std::vector<std::pair<char, int>>, 3>(lambda);
    for(const auto& p : v)
        std::cout << p.first << " = " << p.second << '\n';

    std::cout << "Map:\n";
    auto m = make_sequence<std::map<char, int>, 3>(lambda);
    for(const auto& p : m)
        std::cout << p.first << " = " << p.second << '\n';
}
-1

You can make something like this:

class SomeClass
{
public:
    static char currentChar;
    SomeClass()
    {
        intValue = -1;
        charValue = currentChar;
        currentChar = static_cast<char>(currentChar + 1);
    }
    char charValue;
    int intValue;
};
char SomeClass::currentChar = *"a";

and create vector like this:

std::vector<SomeClass> v(26);

It will create 26 elements, from first to last, and each default constructor will increase letter by 1

MaciekGrynda
  • 583
  • 2
  • 13
  • The OP asked for a simple solution in, ideally, a one-liner. You came up with a full class instead. I think this is too far off from what's being requested and has other implications that are unwarranted. For example, this class does not really represent an object, it's simply trying to do the job of a function... in a ctor... it pollutes the name space for no good reason, uses more resources, etc. Bad bad code. – code_dredd Jun 27 '16 at 10:33