Пакет параметров с совпадением типа предыдущего параметра - PullRequest
0 голосов
/ 18 сентября 2018

Итак, я использую простой пример, чтобы попытаться понять шаблоны с переменным числом аргументов и некоторые приемы 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? Спасибо!

EDIT:

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

Мой подход был

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 Ответ

0 голосов
/ 18 сентября 2018

Вы пытались вместо этого создать шаблон конструктора?

Как:

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&&;)

РЕДАКТИРОВАТЬ: Как сказано в комментарии, этот код не работает, когда вы передаете конструктор Timer. Есть какой-то способ избежать этой проблемы, например, 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>

Название для этого - руководство по выводу.

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