Пакет параметров с совпадением типа предыдущего параметра
Поэтому я использую простой пример, чтобы попытаться понять шаблоны с переменным числом аргументов и некоторые приемы tmp. Пример состоит из класса Timer, у которого есть метод toc(). Методы toc используются для остановки таймера и вызова функции, которая решает, что делать (распечатать, сохранить в переменной...)
Так что я закодировал эту идею следующим образом (я убрал биты синхронизации)
class VerbosePolicy {
public:
VerbosePolicy() {}
explicit VerbosePolicy(const std::string &message) : m_message(message) {}
VerbosePolicy(VerbosePolicy &&other) { m_message = other.m_message; }
void operator()(double time) { std::cout << m_message << time << std::endl; }
private:
std::string m_message;
};
template <typename Policy, typename... Args> class Timer {
public:
Timer(Args... args) : m_policy(Policy(std::forward<Args>(args)...)) {}
void toc(double time) { m_policy(time); }
private:
Policy m_policy;
};
Здесь я создаю Таймер с Политикой и вызываю ctor Полиса с пакетом параметров. Таким образом, я могу контролировать работу Политики (например, я могу передать переменную и сохранить там результат).
Теперь я хочу использовать это
int main(int argc, char **argv) {
std::string string = "Elapsed time";
Timer<VerbosePolicy> timer(string);
timer.toc(1.0);
}
Проблема здесь в том, что компилятор не может определить, является ли строка частью пакета параметров, и пытается сопоставить ее со временем политики, что не удается.
Я попытался добавить аргумент по умолчанию для таймера ctor
Timer(Args... args, Policy policy = Policy())
Но это также не удается, так как он все еще пытается сопоставить de string с типом политики (в этом случае он пытается вызвать второй ctor, который завершается неудачно, потому что он помечен как явный. Если я удаляю его, он компилируется, но работает неправильно, потому что значение политики его неверно).
Все отлично работает если я напишу
Timer<VerbosePolicy, std::string> timer(string)
так как больше не нужно выводить шаблон переменной
В любом случае я могу избежать написания std::string? Спасибо!
РЕДАКТИРОВАТЬ:
Поэтому для полноты и решения некоторых проблем, о которых говорилось в комментариях к действительному ответу, я пытался деактивировать конструктор переменных, когда параметр такого же типа, как и таймер, безуспешно.
Мой подход был
template <typename T, typename... Tail> struct first_of { using type = T; };
template <typename Policy> class Timer {
public:
template <
typename... CArgs,
std::enable_if_t<!std::is_same<Timer<Policy>,
typename first_of<CArgs...>::type>::value,
int> = 0>
Timer(CArgs &&... args) : m_policy(std::forward<CArgs>(args)...) {}
Timer(const Timer<Policy> &other) : m_policy(other.m_policy) {}
void toc(double time) { m_policy(time); }
private:
Policy m_policy;
};
int main(int argc, char **argv) {
std::string string = "Elapsed time";
Timer<VerbosePolicy> timer(string);
Timer<VerbosePolicy> timer2(timer);
timer.toc(1.0);
}
Но компилятор все еще пытается использовать конструктор variadic для timer2. Я не уверен, почему он пытается это сделать, поскольку два типа, передаваемые в std::is_same, должны быть равны, и поэтому ctor должен быть деактивирован.
Что я недопонимаю?
Еще раз спасибо!
1 ответ
Вы пытались сделать шаблон конструктора вместо этого?
Подобно:
template <typename Policy> class Timer {
public:
template<typename ...Args>
Timer(Args && ... args) : m_policy(std::forward<Args>(args)...) {}
void toc(double time) { m_policy(time); }
private:
Policy m_policy;
};
Кстати, вы используете std::forward
неправильно Что вы делаете, это то, что:
template<typename T>
void foo(T v) {
std::forward<T>(v);
}
В таком коде T
это не эталонное значение. Так что переслать это здесь означает: T&&
так же, как "двигаться"
если вы хотите переслать ссылку, вы должны использовать ссылку пересылки:
template<typename T>
void foo(T &&v) {
std::forward<T>(v);
}
Если аргумент является ссылкой lvalue, здесь T
это T&
и если аргумент является ссылкой на Rvalue, T
это T
и путем пересылки ссылка V соответственно T& &&
так T&
а также T &&
так T&&
;)
РЕДАКТИРОВАТЬ: Как сказано в комментарии, этот код не работает, когда вы даете таймер конструктору. Есть какой-то способ избежать этой проблемы, например, SFINAE может помочь вам;)
РЕДАКТИРОВАТЬ 2: Вы хотите отслеживать ваши Args ...
как вы сказали в комментарии.
Допустим, у вас есть такой класс:
template<typename ...Args>
class Foo {
public:
Foo(Args... args) : noexcept m_tuple{std::move(args)...} {}
private:
std::tuple<Args...> m_tuple;
};
Вы хотите вывести тип: есть два способа:
1) До C++ 17:
template<typename ...Args>
Foo<Args...> make_foo(Args ...args) {
return {args...};
}
auto d = make_foo(5, 3.0); // Foo<int, double>
2) после с ++ 17
template<typename ...Args>
Foo(Args...) -> Foo<Args...>;
Foo foo{3.0, "Lol"s}; // Foo<double, std::string>
Название для этого - руководство по выводу.