Списки нулевой стоимости для встроенных функций в C ++ - PullRequest
0 голосов
/ 04 февраля 2019

Мне нравится писать проверки для функции над списком.Для этого я обычно пишу такую ​​функцию:

inline bool good_strings(const std::vector<const char *> & items)
{
    for (i in items) {
        if (not is_good(i)) return false;
    }
    return true;
}

Затем я могу написать как if (all_good({"a", "b", "c", "d", "e"})) {...}, и это выглядит действительно хорошо.Это полезно использовать, когда ваш чек на пару предметов становится больше, например:

if (is_good("a") and is_good("b") and /* that's too much, man */ is_good("c")) {...}

Но меня беспокоит накладные расходы на контейнер, который я использую, а также трудно выбрать один: std::vector, std::list, QList, QStringList или, может быть, даже std::array или std::initializer_list - , которые следует использовать для встроенных функций ?И какой из них имеет минимальные или даже нулевые накладные расходы при создании с использованием {} скобок?

Хорошо, и обновление: я взял моего друга лицензированную IDA Pro и проверил некоторые опции.

  • std::initializer_list: функция даже не встроена, и есть издержки на создание списка и копирование указателей.
  • std::vector: функция встроенная, однакоэто накладные расходы на создание вектора и копирование там указателей.
  • std::array: не так хорошо выглядит из-за специализации шаблона, и функция не встроена.Так что многократное обращение к нему создает много похожих кусков кода.Тем не менее, нет никаких накладных расходов на создание массива, и все указатели передаются как параметры функции, что очень быстро для x86_64 регистра, вызывающего соглашение.

Остается вопрос, есть лиКонтейнер абсолютно нулевой стоимости ?

Ответы [ 4 ]

0 голосов
/ 05 февраля 2019

Если вы действительно озабочены использованием контейнера, вы можете просто написать N перегрузки, например, для N = 5:

inline bool good_string(const char* a)
{
    return true;
}
inline bool good_strings(const char* a, const char* b)
{
    return good_string(a) && good_string(b);
}
inline bool good_strings(const char* a, const char* b, const char* c)
{
    return good_strings(a, b) && good_string(c);
}
inline bool good_strings(const char* a, const char* b, const char* c, const char* d)
{
    return good_strings(a, b, c) && good_string(d);
}
inline bool good_strings(const char* a, const char* b, const char* c, const char* d, const char* e)
{
    return good_strings(a ,b, c, d) && good_string(e);
}
0 голосов
/ 04 февраля 2019

Если вы шаблонируете функцию следующим образом:

bool is_good(const std::string &) { return true; )
template<typename Container>
inline bool good_strings(const Container & items)
{
    for (auto const &i : items){
        if (not is_good(i)) return false;
    }
    return true;
}

, то вызов good_strings(std::initializer_list<std::string>{"a", "b", "c", "d", "e"}) передаст список инициализатора на all_good.Контейнер не требуется.

0 голосов
/ 04 февраля 2019

Хорошо, благодаря идеям в предыдущих ответах, я понял это.Если то, что люди говорят о "не существует лучших контейнеров", то std :: array является лучшим.При компиляции с уровнем оптимизации -O2, std::array не имеет ни копирования параметров, ни вызова функций, в то время как std :: initializer_list имеет копирование параметров.При компиляции с -O0 это все, как я описал в самом вопросе.

Итак, мое решение: используйте std::array и справьтесь с указанием <N> для количества аргументов.

0 голосов
/ 04 февраля 2019

Ни один из контейнеров не будет иметь нулевые накладные расходы.std::array или std::initializer_list даст вам наименьшую сумму стоимости, хотя.std::array необходимо указать его тип и размер во время компиляции, поэтому он немного менее удобен для пользователя, чем std::initializer_list в этом случае.Таким образом, использование std::initializer_list<const char*> будет самым маленьким и простым в использовании «контейнером», который вы можете использовать.Это будет стоить размера массива указателей, генерируемых компилятором, и, возможно, немного больше, и не потребует динамического выделения памяти.


Если вы можете использовать C ++ 17, вы не будетедаже нужен контейнер.Используя шаблон переменной и сложенное выражение , вы можете передать все аргументы функции в виде отдельных параметров и применить одну и ту же операцию ко всем аргументам.

template<typename... Args>
bool good_strings(Args&&... args)
{
    return (is_good(args) && ...);
}

превратит

all_good("a", "b", "c", "d", "e")

в

return is_good("a") && is_good("b") && ... && is_good("e");

, что использует короткое замыкание, поэтому оно прекратит оценку, как только первый вызов is_good вернет false.

Вы можете использовать шаблон variadic в C ++ 11, но вам нужно будет либо использовать рекурсию, либо создать свой собственный массив, который на самом деле ничего не даст вам с такой дополнительной сложностью, как у вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...