Функція шаблону не працює для функції вказівника на члена, яка приймає const ref


14

Нещодавно я написав функцію шаблону для вирішення деяких повторень коду. Це виглядає приблизно так:

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(fun, *sp, args...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

int main() {
    auto a = std::make_shared<A>();
    call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}

Цей код прекрасно працює, class Aякий виглядає приблизно так:

class A {
public:
    void foo(int x) {

    }
};

Але не вдається компілювати для такого, як це:

class A {
public:
    void foo(const int& x) {

    }
};

Чому так (через те, чому я маю на увазі, чому він не виводить тип) і як (якщо це взагалі можливо) я можу змусити цей код працювати з посиланнями? Живий приклад


можливо, Args&&...і std::forward?
FAS

@ user3365922 спробував це. Відчувається, що рішення не працює
bartop

Чи не буде чи це і це допоможе вам в правильному напрямку?
Gizmo

Відповіді:


3

Ваша проблема полягає в тому, що у вас є конфліктні відрахування Argsміж:

  • R (T::*fun)(Args...)
  • Args... args

Я пропоную , щоб мати більше загальний код (відсутність дублювання між R (T::*fun)(Args...)і
версією сопзЬ R (T::*fun)(Args...) constі іншою альтернативою) з:

template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
                             const std::string& error,
                             F f,
                             Args&&... args)
{
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(f, *sp, std::forward<Args>(args)...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

хороший момент щодо cv-кваліфікації функції члена, я вважаю, що це найкраще рішення поки що
bartop

8

Argsтипи не можуть бути виведені як const&funоголошення параметра), так і без посилання від argsдекларації. Просте виправлення полягає у використанні двох окремих пакетів параметрів типу шаблону:

template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
    const std::weak_ptr<T>& ptr,
    const std::string& error,
    R (T::*fun)(DeclaredArgs...),
    Args... args);

Як мінус, я можу уявити трохи довші повідомлення про помилки у разі поганого використання.


1
Ви, мабуть, хочетеArgs&&... args
Jarod42

5

Зауважте, що Argsтип параметра шаблону виводиться, як const int&на третьому аргументі функції &A::foo, і виводиться як intна 4-му параметрі функції 1. Вони не відповідають і спричиняють невдачу.

Ви можете виключити четвертий параметр від вирахування , наприклад

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, 
                const std::string& error, 
                R (T::*fun)(Args...), 
                std::type_identity_t<Args>... args) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

ЖИВИЙ

PS: std::type_identityпідтримується з C ++ 20; але реалізувати це досить просто.


1
це буде якось працювати з ідеальним переадресацією?
bartop

@bartop Я так думаю. Ми можемо зробити 4-й параметр, відповідний стилю пересилання, тобто Args&&..., потім поставити std::type_identityна 3-й параметр, як R (T::*fun)(std::type_identity_t<Args>...). LIVE and LIVE
songyuanyao

@songyuanyo Так, але тоді воно порушиться для аргументу значення.
bartop

Ви вже можете використовувати вперед з коду Demo . Це просто зробить "зайвий" хід.
Jarod42
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.