Как обернуть функцию переменным числом аргументов по умолчанию, чтобы иметь только один аргумент?

У меня есть функция шаблона, давайте назовем ее "клиент":

template<typename T>
void client(T (*func)(const std::string&), const std::string& s) {}

Кроме того, существует ряд "адаптируемых" функций, которые имеют идентичный тип первого аргумента, отличного от значения по умолчанию, но следующие аргументы различаются по количеству и имеют значения по умолчанию:

void adaptee_one(const std::string&, int i = 1, char* c = nullptr) {}
void adaptee_two(const std::string&, float* f = nullptr) {}

Вышеуказанные функции даны. Теперь я хочу передать их вышеупомянутым client<>() функция в качестве первого параметра, и я забочусь только о передаче первого аргумента, const std::string&, Поэтому я делаю следующее:

void bindAdapteeOne(const std::string& s) {
    return adaptee_one(s);
}

void bindAdapteeTwo(const std::string& s) {
    return adaptee_two(s);
}

А потом пройти bindAdapteeX() в client<>(),

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

C++ 11 в порядке, C++14 в порядке, если это абсолютно необходимо.

3 ответа

C++11 в порядке, C++14 в порядке, если это абсолютно необходимо.

C++11 решение здесь.

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

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

client (+[](const std::string& s) { return adaptee_one(s); }, "foo");

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


Как минимальный рабочий пример:

#include<string>

template<typename T>
void client(T (*func)(const std::string&), const std::string& s) {}

void adaptee_one(const std::string&, int i = 1, char* c = nullptr) {}
void adaptee_two(const std::string&, float* f = nullptr) {}

int main() {
    client (+[](const std::string& s) { return adaptee_one(s); }, "foo");
}

Это один из тех случаев, когда макрос помогает:

#define WRAP_FN(f) +[](std::string const& s) -> decltype(auto) { return f(s); }

Хотя вы могли бы просто написать тело этого inline вместо этого.


Больше ты ничего не сможешь сделать. Проблема в том, что аргументы по умолчанию не видны в сигнатуре функции, поэтому, как только вы попадете в систему типов, нет никакой возможности провести различие:

void works(std::string const&, int=0);
void fails(std::string const&, int  );

Оба из них void(*)(std::string const&, int), Таким образом, у вас не может быть шаблона функции или обертки шаблона класса - вам нужно сделать это inline с лямбда-выражением (или макросом, который оборачивает лямбда-выражение).

Я думаю, я бы создал класс, который обернет ваши параметры, и пусть клиент примет экземпляр этого класса. Таким образом, у вас есть только один параметр, который содержит столько параметров, сколько вы пожелаете.

Этот упаковщик параметров также предоставит значения по умолчанию и позволит вам уточнить их в производных классах для конкретных целей.

Это, вероятно, немного более самодокументирующееся по сравнению с лямбдами.

И кто знает, когда пора читать и записывать параметры из файла, тогда класс-обертка будет идеальным местом для этого.

Другие вопросы по тегам