operator›› на связанном кортеже с std::ignore

Я наткнулся на следующую проблему при разработке «общего» читателя:

Следующий код работает отлично (вам нужна поддержка С++ 1z для компиляции, поскольку он использует constexpr if, но с небольшими изменениями он также должен компилироваться с С++ 11):

#include <vector>
#include <string>
#include <type_traits>
#include <tuple>
#include <sstream>

using namespace std;

template<int N, class tuple_type>
struct fill_tuple {
    static void write(std::vector<std::string>& container, tuple_type& tuple)
    {
        // use operator >> to fill the N-1'th member of the tuple
        std::stringstream(container[N - 1]) >> std::get<N - 1>(tuple);
        if constexpr(N > 1){ // Continue if there are till fields to read
            fill_tuple<N - 1, tuple_type>::write(container, tuple);
        }
    }
};

template<class tuple_type>
void read (std::vector<std::string>& container, tuple_type obj){
    fill_tuple<std::tuple_size<tuple_type>::value, tuple_type>::write(container, obj);
}

struct some_data {
    char a;
    char b;
    char c;
    char d;

    auto content() {
        return std::tie(a,b,c,d);
    }
};

int main()
{
    std::vector<std::string> some_strings = {"a","b","c","d"};
    // Read some_strings into some_data
    some_data foo;
    read(some_strings, foo.content());
}

Для простоты любые связанные проверки (такие как tuple_size ‹= размер контейнера) опущены.

Если бы я хотел разобрать структуру, которая имеет только элементы a, b и d, используя контейнер размером 4, моя интуиция заключалась в том, чтобы просто переписать std::tie(a,b,c,d) в std::tie(a,b,std::ignore,d).

Это, однако, терпит неудачу, поскольку std::ignore (или реализация gcc), похоже, не имеет функции operator>>. Я уже пытался проверить std::ignore с помощью std::is_same: std::is_same<typename std::remove_reference<typename std::tuple_element<N - 1,tuple_type>::type>::type, std::ignore>::value, но это тоже не помогло.

Мой вопрос: есть ли способ проверить std::ignore или, что еще лучше, полностью заменить его, не полагаясь на предыдущие модификации вектора контейнера?


person patrick Klein    schedule 15.12.2017    source источник
comment
std::ignore подобен объекту, а не типу. Попробуйте поставить decltype вокруг него.   -  person Incomputable    schedule 15.12.2017
comment
Следующий код работает отлично, даже без ; после объявления структуры?   -  person Edgar Rokjān    schedule 15.12.2017
comment
Извините, @EdgarRokyan, я отредактировал код, чтобы он действительно скомпилировался.   -  person patrick Klein    schedule 15.12.2017
comment
не полагаясь на предварительные модификации вектора контейнера, что вы имеете в виду?   -  person Massimiliano Janes    schedule 15.12.2017
comment
@Incomputable - ИМХО, ваш комментарий должен быть ответом   -  person max66    schedule 15.12.2017
comment
@ max66, позвольте мне найти некоторые ссылки, и я опубликую их как ответ.   -  person Incomputable    schedule 15.12.2017
comment
@MassimilianoJanes Я не хочу предварительно обрабатывать std::vector<std::string> container. То есть я не хочу просто удалять элемент 'c' из some_strings в данном примере.   -  person patrick Klein    schedule 15.12.2017


Ответы (2)


полностью заменить его, не полагаясь на предварительные модификации вектора-контейнера?

вместо использования is_same вы можете просто перегрузить decltype(ignore); в С++ 17:

template<typename T>
void read_element( std::string const& s, T& t ) { std::stringstream{s} >> t; }
void read_element( std::string const&, decltype(std::ignore) const& ) { /*do nothing*/ }

std::apply( [&](auto&... args)
        {
            auto it = some_vector_of_strings.begin();
            ( read_element( *it++, args ), ... );
        }, tuple );

та же идея применима и к вашему коду C++11.

person Massimiliano Janes    schedule 15.12.2017
comment
Отправка на основе тегов все еще работает, лол. Несмотря на то, что с ним сложнее иметь дело. - person Incomputable; 15.12.2017
comment
@incomputable да, диспетчеризация тегов может быть даже более подходящей, если требуется более сложная логика read_element. Тем не менее, я считаю, что первоначальное намерение OP состояло в том, чтобы использовать здесь if constexpr. - person Massimiliano Janes; 15.12.2017
comment
Мне немного стыдно, что я не сам это придумал. В любом случае, мне очень нравится ваш ответ, но для протокола, в моем конкретном случае он должен быть decltype(std::ignore)&. - person patrick Klein; 15.12.2017
comment
@patrickklein, вы правы, даже лучше decltype(std::ignore) const&; исправлено - person Massimiliano Janes; 15.12.2017

Как указано в комментарии, std::ignore - это не тип, а объект, вы должны использовать decltype(std::ignore), чтобы получить тип.

template <typename T>
void read_simple(const std::string& s, T& obj)
{
    std::stringstream(s) >> obj;   
}

void read_simple(const std::string&, const decltype(std::ignore)&) {}

template <std::size_t ... Is, typename Tuple>
void read(const std::vector<std::string>& container,
          Tuple&& obj,
          std::index_sequence<Is...>)
{
    (read_simple(container[Is], std::get<Is>(obj)), ...);   
}

template <typename Tuple>
void read(const std::vector<std::string>& container, Tuple&& obj)
{
    read(container,
         obj,
         std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}

Демо

person Jarod42    schedule 15.12.2017
comment
для записи, AFAIK тип ignore не может быть скопирован, поэтому его следует передать по ссылке для переносимости... - person Massimiliano Janes; 15.12.2017
comment
@MassimilianoJanes: исправлено. - person Jarod42; 15.12.2017