Вывод типа для шаблонного класса, принимающего лямбду - PullRequest
1 голос
/ 16 апреля 2019

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

Я использую VC ++ 2017, с включенным набором инструментов C ++ 17.Это ожидаемое поведение?Зачем?Можно ли избежать фабричной функции или это необходимо из-за различных правил вывода типов для шаблонных функций и шаблонных классов?Любая помощь будет оценена.

template <typename F>
class WillInvoke
{
public:
    WillInvoke(std::decay_t<F> f) : f(std::move(f)) { }

    void CallNow() { f(); }

private:
    std::decay_t<F> f;
};

template <typename F>
WillInvoke<F> make_WillInvoke(F && f)
{
    return WillInvoke<F>(std::forward<F>(f));
}

int main()
{
    // OK
    auto w = make_WillInvoke([](){ std::cout << "Hello World"; });
    w.CallNow();

    // Won't compile
    WillInvoke w2([](){ std::cout << "Hello World"; }); // No instance of constructor matches argument list
    w2.CallNow();
}

1 Ответ

5 голосов
/ 16 апреля 2019

Это потому, что псевдоним типа члена, такой как std::decay<T>::type, не подлежит вычету.

template<typename T>
void call_me(std::decay_t<T>) {}

// won't work :(
// call_me(1);

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

template <typename F> // requires std::is_object_v<F>
class WillInvoke
{
    static_assert(std::is_object_v<F>,
        "WillInvoke requires F to be an object type"
    );
public:
    WillInvoke(F f) : f(std::move(f)) { }

    void CallNow() { f(); }

private:
    F f;
};

template <typename F>
auto make_WillInvoke(F && f) -> WillInvoke<std::decay_t<F>>
{
    return WillInvoke<std::decay_t<F>>(std::forward<F>(f));
}

Приятно то, что в C ++ 20 вы можете раскомментировать require и позволить компиляторуприменить это на сайте вызова.

...