Const ref alan işaretçiden üyeye işlev için şablon işlevi çalışmıyor


14

Son zamanlarda bazı kod tekrarlarını çözmek için bir şablon fonksiyonu yazdım. Şöyle görünüyor:

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

Bu kod class A, şuna benzeyen mükemmel bir şekilde çalışır :

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

    }
};

Ancak böyle bir şey için derlenemez:

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

    }
};

Neden böyle (neden türünü çıkarmayı başarısızlıkla ifade ettiğimi kastediyorum) ve nasıl (mümkünse) bu kodu referanslarla çalıştırabilirim? Canlı örnek


belki Args&&...ve std::forward?
fas

@ user3365922 denedi. Çözüm gibi geliyor, çalışmıyor
bartop

Bu ve bu size doğru yönde yardımcı olmaz mı?
Gizmo

Yanıtlar:


3

Sorununuz Argsarasında aşağıdakiler için çakışma kesintileri olması :

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

Daha genel bir kod (arasında hiçbir tekrarları var önermek R (T::*fun)(Args...)ve
const sürümü R (T::*fun)(Args...) constile ve diğer alternatif):

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

üye işlevinin cv-yeterlilik hakkında iyi bir nokta, bu şimdiye kadar en iyi çözüm olduğunu düşünüyorum
bartop

8

Argstürler hem const&( funparametre bildiriminden) hem de bildirimden referans gösterilmeden çıkarılamaz args. Basit bir düzeltme, iki ayrı şablon türü parametre paketi kullanmaktır:

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

Bir dezavantaj olarak, kötü kullanım durumunda biraz daha uzun hata mesajları hayal edebiliyorum.


1
Muhtemelen istiyorumArgs&&... args
Jarod42

5

Şablon parametresinin Argstürünün const int&3. işlev bağımsız değişkeninde &A::fooolduğu ve int4. işlev parametresinde olduğu gibi düşüldüğünü unutmayın 1. Eşleşmezler ve kesinti başarısızlığına neden olurlar.

4. parametreyi tümdengelimden hariç tutabilirsiniz , ör.

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

CANLI

PS: std::type_identityC ++ 20'den beri desteklenmektedir; ancak bir tane uygulamak oldukça kolaydır.


1
bir şekilde mükemmel yönlendirme ile çalışacak mı?
bartop

@bartop Sanırım. 4. parametreyi yönlendirme referans stiline uygun hale getirebiliriz, yani 3. parametreyi aşağıdaki gibi Args&&...koyabiliriz . CANLI ve CANLIstd::type_identityR (T::*fun)(std::type_identity_t<Args>...)
songyuanyao

@songyuanyo evet, ama sonra değer argümanı için kırılacak.
bartop

Kod Demo'nuzdan ileriye doğru kullanabilirsiniz . Sadece "ekstra" hareket yapacak.
Jarod42
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.