Использование шаблонов в std :: conditional для определения типов аргументов функции - PullRequest
0 голосов
/ 31 октября 2019

Я хочу, чтобы все мои сохранения и загрузки данных проходили через одни и те же функции, чтобы уменьшить вероятность ошибок. Для этого я использовал много шаблонов (и много перегрузок функций). Это сработало, мой код теперь намного чище, но я не смог использовать const для сохранения (потому что он выполняет те же функции, что и загрузчик, где данные хранятся неконстантно).

Я хотел бы использовать const правильно, поэтому здесь делается попытка заставить работать простую версию, где данные (в данном случае std::vector) неконстантны для std::ifstream, а const в противном случае:

#include <iostream>
#include <fstream>
#include <vector>

template <class Foo>
void Overload(const Foo & foo)
{
    std::cout << "went to const" << std::endl;
}

template <class Foo>
void Overload(Foo & foo)
{
    std::cout << "went to non-const" << std::endl;
}

template <class StreamType, typename... Arguments>
void ReadOrWrite (

    /* for 1st argument */ StreamType & filestream,

    /* type for 2nd argument */ typename std::conditional< 
        /* if */    std::is_same<StreamType, std::ifstream>::value,
            /* then */  std::vector<Arguments...>,
            /* else */  const std::vector <Arguments...>
            >::type

        /*2nd argument name */ & vector
        )
{
    Overload(vector);
}

int main ()
{
    std::ofstream output_filestream;
    std::ifstream intput_filestream;

    std::vector<int> vector;

    ReadOrWrite(output_filestream, vector);
    ReadOrWrite(intput_filestream, vector);

    return 0;
}

Я знаю, что он будет правильно скомпилирован / запущен, если я отредактирую вызовы функций для этого:

ReadOrWrite<std::ofstream, int>(output_filestream, vector);
ReadOrWrite<std::ifstream, int>(intput_filestream, vector);

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

Есть ли чистый способ сделать то, что я предлагаю?

РЕДАКТИРОВАТЬ

Кажется, естьвопрос о законности моего мотива.

Я не объяснил свой мотив полностью, потому что он не слишком прост (и не слишком сложен), и я уважаю время читателей.

ПримерЯ дал, был голый компонент того, что я не смог решить - и "оФункции verload были просто включены, чтобы посмотреть, сработало ли это.

Однако, похоже, мое отсутствие объяснения вызвало путаницу, поэтому я поясню:

Я сделал небольшую библиотеку для обработкиобщее сохранение и загрузка данных. Он успешно позволяет классам пользователей иметь простые методы сохранения / загрузки с помощью следующего интерфейса:

class SomeClass
{
public:

    template <class StreamType>
    void SaveOrLoad(StreamType & filestream)
    {
        saveload::SaveToOrLoadFromFile(filestream,

            data_1_,
            data_2_,
            /* ..., */
            data_n_,
        );
    }

    void SaveToFile (const std::string & filename)
    {
        std::ofstream output_filestream(filename, std::ios::binary);

        // file handling

        SaveOrLoad(output_filestream);
    }

    void LoadFromFile (const std::string & filename)
    {
        std::ifstream input_filestream(ptf::problem_input_file, std::ios::binary);

        // file handling

        SaveOrLoad(input_filestream);
    }
};

Эта библиотека обрабатывает все основные типы данных, контейнеры STL и любые другие контейнеры, которые используют правильный SaveOrLoad(StreamType &) интерфейс, включая сохранение и изменение размера всех контейнеров. Библиотека заставила все сохранения и загрузки проходить одни и те же детерминированные функции и, следовательно, полностью устранила возможность ошибок, связанных с несовпадением сохранения / загрузки (если пользователь не использует простой интерфейс библиотеки).

проблема, с которой я столкнулся в своей библиотеке - и, следовательно, причина моего вопроса - является теоретической, потому что в настоящее время она мне не нужна: метод SaveToFile должен быть в состоянии const.

Ответы [ 2 ]

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

Лучшим предложением было бы предоставить две отдельные функции, поскольку чтение и запись - это две разные операции, независимо от того, какой тип вы в них отправили. Например, кто-то может быть fstream для ввода и вывода. Просто по системе типов вы не можете знать намерения. Решение о чтении или записи обычно является намерением, и они редко встраиваются в систему типов.

Поскольку сохранение и загрузка являются отдельной операцией, это должны быть разные функции (возможно, совместное использование кода между ними)


Если вам действительно нужна функция, которая выполняет оба действия и переключается между типами, то я бы посоветовала ограничить функции для ввода или вывода:

// output streams
template <class StreamType, typename... Arguments,
    typename std::enable_if<std::is_base_of<std::ostream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
    StreamType & filestream,
    std::vector<Arguments...> const& vector
) {
    Overload(vector);
}

// input streams
template <class StreamType, typename... Arguments,
    typename std::enable_if<std::is_base_of<std::istream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
    StreamType& inputstream,
    std::vector<Arguments...>& vector
) {
    Overload(vector);
}

Поскольку вторая более специализирована, чемво-первых, он будет взят всякий раз, когда поток равен std::istream, а вектор изменчив. В противном случае берется первый.

Живой пример

2 голосов
/ 31 октября 2019

Другое решение по перегрузке может преобразовать ReadOrWrite(), почти как вы написали в своем вопросе, в вспомогательную функцию

template <typename ... Args, typename ST>
void ReadOrWrite_helper (ST &, typename std::conditional<
                                  std::is_same<ST, std::ifstream>::value,
                                  std::vector<Args...>,
                                  std::vector<Args...> const>::type vec)
 { Overload(vec); }

, добавив перегруженную пару функций ReadOrWrite() для выбора выбора Args...и явным образом вызывая вспомогательную функцию

template <typename ... Ts>
void ReadOrWrite (std::ifstream & is, std::vector<Ts...> & vec)
 { ReadOrWrite_helper<Ts...>(is, vec); }

template <typename ... Ts>
void ReadOrWrite (std::ofstream & is, std::vector<Ts...> const & vec)
 { ReadOrWrite_helper<Ts...>(is, vec); }

Заметьте, что, учитывая, что типы Args... находятся в не выводимом контексте, поэтому их необходимо объяснить, я поместил их в объявление параметра шаблона ReadOnWrite_helper() до ST;поэтому нет необходимости также явно указывать ST.

Обратите внимание, что если вам не нужно знать типы Args... внутри ReadOrWrite_helper(), все станет проще

template <typename V, typename ST>
void ReadOrWrite_helper (ST &, V & vec)
 { Overload(vec); }

template <typename V>
void ReadOrWrite (std::ifstream & is, V & vec)
 { ReadOrWrite_helper(is, vec); }

template <typename V>
void ReadOrWrite (std::ofstream & is, V const & vec)
 { ReadOrWrite_helper(is, vec); }

а также исчезает необходимость объяснения типа V.

...