Hàm mẫu không hoạt động đối với hàm con trỏ đến thành viên lấy const ref


14

Gần đây tôi đã viết một hàm mẫu để giải quyết một số lần lặp lại mã. Nó trông như thế này:

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

Mã này hoạt động hoàn toàn tốt class Amà trông như thế này:

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

    }
};

Nhưng không thể biên dịch cho một cái như thế này:

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

    }
};

Tại sao nó lại như vậy (tại sao tôi có nghĩa là tại sao nó không thể suy ra loại) và làm thế nào (nếu có thể) tôi có thể làm cho mã này hoạt động với các tham chiếu? Ví dụ sống


có thể Args&&...std::forward?
fas

@ user3365922 đã thử nó. Cảm thấy giống như giải pháp, không hoạt động
bartop 23/10/19

Sẽ không phải nàynày giúp bạn đi đúng hướng?
Gizmo

Câu trả lời:


3

Vấn đề của bạn là bạn có các khoản khấu trừ xung đột Argsgiữa:

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

Tôi đề nghị để có mã chung chung hơn (không trùng lặp giữa R (T::*fun)(Args...)
phiên bản const R (T::*fun)(Args...) constvà thay thế khác) với:

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());
    }
}

điểm tốt về trình độ cv của chức năng thành viên, tôi nghĩ rằng đây là giải pháp tốt nhất cho đến nay
bartop

8

Argscác loại không thể được suy ra cả const&(từ funkhai báo tham số) và không tham chiếu từ argskhai báo. Một sửa chữa đơn giản là sử dụng hai gói tham số loại mẫu riêng biệt:

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

Như một nhược điểm, tôi có thể tưởng tượng các thông báo lỗi dài hơn một chút trong trường hợp sử dụng xấu.


1
Bạn có thể muốnArgs&&... args
Jarod42

5

Lưu ý rằng loại của tham số mẫu Argsđược suy ra như const int&trên đối số hàm thứ 3 &A::foovà được suy ra như inttrên tham số hàm thứ 4 1. Họ không khớp và gây ra thất bại.

Bạn có thể loại trừ tham số thứ 4 khỏi khấu trừ , vd

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) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

TRỰC TIẾP

PS: std::type_identityđược hỗ trợ kể từ C ++ 20; nhưng nó khá dễ thực hiện.


1
nó sẽ làm việc với chuyển tiếp hoàn hảo bằng cách nào đó?
bartop

@bartop Mình nghĩ vậy. Chúng ta có thể làm cho tham số thứ 4 phù hợp với kiểu chuyển tiếp tham chiếu, nghĩa là Args&&..., sau đó đặt std::type_identityvào tham số thứ 3 như thế nào R (T::*fun)(std::type_identity_t<Args>...). TRỰC TIẾPSỐNG
songyuanyao

@songyuanyo yeah, nhưng sau đó nó sẽ phá vỡ đối số giá trị.
bartop

Bạn đã có thể sử dụng chuyển tiếp từ Demo mã của bạn . Nó sẽ chỉ làm "di chuyển" thêm.
Jarod42
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.