Два типа вариационного расширения - PullRequest
2 голосов
/ 30 октября 2019

Как можно сделать два типа вариационного расширения? Вот что я пытаюсь достичь:

#include <vector>
#include <iostream>

class test
{
public:
    std::vector< std::pair< float, int > > vec;
    template<typename... T1, typename... T2>
    test( T1... one, T2... two )
    {
        (
            [&](float first, int second)
            {
                vec.emplace_back( std::pair< float, int >( first, second ) );
                std::cout << first << ", " << second << std::endl;
            }( one, two ),
            ...
        );
    }
};

int main()
{
    test t
    {
        1.f,
        1,
        2.f,
        2,
        3.f,
        3
    };

    return 0;
}

test должно быть инициализировано точно так же, как в main. Я хотел бы, чтобы использование в конструкторе теста оставалось схожим.

Вот рабочая концепция с va_list. К сожалению, мне нужно передать счет для параметров или передать терминатор магического числа (я выбрал терминатор магического числа).

#include <cstdarg>
#include <iostream>
#include <vector>

constexpr int END_OBJECT = 890123; // magic number

class test {
 public:
  std::vector<std::pair<int, double>> vec;

  enum { is_name, is_data, is_max };

  test(char ch, ...) {
    std::pair<int, double> buf;

    va_list b;
    va_start(b, ch);
    for (int i = 0;; i++) {
      auto is = i % is_max;

      if (is == is_name) {
        if ( (buf.first = va_arg(b, int)) == END_OBJECT )
            break;
      } else if (is == is_data) {
        buf.second = va_arg(b, double);
        vec.emplace_back(buf);
      }
    }
    va_end(b);
    std::cout << ch << std::endl;
    for (auto &x : vec)
      std::cout << '\t' << x.first << ", " << x.second << std::endl;
  }
};

int main() {
  test t
  {
    'x',
    1,
    2.0,
    3,
    4.0,
    5,
    6.0,
    END_OBJECT
  };

  return 0;
}

Я бы хотел более современную версиюэто с использованием пакета расширения.

1 Ответ

3 голосов
/ 30 октября 2019

Забавно, что это в основном FizzBuzz с аргументами шаблона, и это действительно хорошая задача.

Самый простой способ в C ++ 14, который я мог придумать, - это использовать std :: index_sequence. https://godbolt.org/z/dm3F9u

#include <vector>
#include <utility>
#include <tuple>

template <typename... TArgs, size_t... Is>
std::vector<std::pair<float, int>> pair_off(std::tuple<TArgs...> args, std::index_sequence<Is...>) {
    return std::vector<std::pair<float, int>> { std::make_pair(std::get<(Is << 1)>(args), std::get<((Is << 1) + 1)>(args))... };
}


template <typename... TArgs>
std::vector<std::pair<float, int>> pair_off(TArgs&&... args) {
    return pair_off(std::forward_as_tuple(std::forward<TArgs>(args)...), std::make_index_sequence<(sizeof...(TArgs) >> 1)>{});
}

std::vector<std::pair<float, int>> test() {
    return pair_off(1.1f, 1, 2.2f, 2, 3.3f, 3);
}

По сути, сначала вы упаковываете аргументы в кортеж. Затем вы делаете последовательность индексов, равную половине размера списка аргументов. Затем вы расширяете эту индексную последовательность, передавая ее в std :: get.

Что это делает, это эквивалент шаблона:

for (int i=0; i<list.size()/2; i++) { 
    output.push_back( std::make_pair(list[i*2], list[i*2+1]) ); 
}
...