Soru Std :: forward uygulaması


okuyorum Yeni C ++'ya Genel Bakış (C ++ 11/14) (yalnızca PDF)Slide 288'de bir uygulama sağlar. std::forward:

template<typename T>                // For lvalues (T is T&),
T&& std::forward(T&& param)         // take/return lvalue refs.
{                                   // For rvalues (T is T),
    return static_cast<T&&>(param); // take/return rvalue refs.
}

Ve sonra metinde başka bir uygulama sunar:

Normal std :: forward uygulaması:

template<typename T>
struct identity {
    typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{ return static_cast<identity<T>::type&&>(param); }

Fark ne? İkinci uygulama neden normaldir?


21
2017-12-16 09:32


Menşei


İlk durumda, sen kaçabilirsin std::forward(x)Ancak, ikinci olarak, çıkarılamayacakları için şablon parametrelerini açıkça belirtmeniz gerekir. - Walter
@PiotrS. Hayır, yapmıyorsun. Neden bu gerekli? Referans daraltma, hızlı iletme referans içeriğinin dışında çalışır. - Sebastian Redl
@SebastianRedl kimliğini kaçırdı, remove_reference - Piotr Skotnicki
@PiotrS. Haklısın, lvalelerin kabul edilmesi gerekiyor. Ancak, rıhtımların olması gerekmez, bu yüzden çözüm ikinci bir aşırı yük değildir, çözüm her zaman orada bulunan bir referansa atıfta bulunmaktır. - Sebastian Redl
@PiotrS. Yanlış olduğum ortaya çıkıyor, standart kabul edilmek için rezonans gerektiriyor. - Sebastian Redl


Cevaplar:


İlk sorun, yazabileceğiniz std::forward(x)İstediğinizi yapmaz, çünkü her zaman lvalue referanslar üretir.

İkinci davadaki argüman, argümandan çıkarılan bir bağlamdır ve şablon argümanının otomatik olarak kesilmesini önler. Bu sizi yazmaya zorluyor std::forward<T>(x)Yapılması gereken doğru şey.

Ayrıca, ikinci aşırı yük için argüman türü typename identity<T>::type& çünkü deyimsel kullanımı için girdi std::forward her zaman bir değerdir.

Düzenle: Standart aslında buna eşdeğer bir imzayı şart koşar (ki, bu, tesadüfi olarak, tam olarak libc ++'nin sahip olduğu şeydir):

template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;

17
2017-12-16 09:36



Parametrede neden kimlik kullanıldığını anlıyorum, ama neden sadece değil return static_cast<T &&>(param);? - kec
Kec isimlilik işlevleri olmadan isimlilik işlevleri olmadan hareket semantiği ve mükemmel yönlendirme öğretmek çok daha zordur static_cast<T&&>(param) T'nin evrensel bir referans olup olmamasına bağlı olarak bir hareket veya ileriye doğrudur. Yardımcı fonksiyonlar okunabilirliğe yardımcı olur ve kazara hareketleri önler. - Barrett Adair


Libc ++ uygulamasında kullanım std::remove_reference ve iki aşırı yüklenme. İşte kaynak (bazı makroları çıkardıktan sonra):

template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept
{
    return static_cast<T&&>(t);
}

template <class T>
inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept
{
    static_assert(!std::is_lvalue_reference<T>::value,
                  "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}

ama C ++ 14’te std::forward olduğu constexpr.


9
2017-12-16 09:50



Static_assert başarısız olabilecek gerçek bir senaryo var mı? - xin wang


Sebastian Redl'in söylediği ilk dava, her zaman size bir referans verilecektir. Nedeni, parametredeki bir rvalue referansının bir lvalue referansı olarak geçirilmesi ve parametre T&& tipi bir evrensel referans rvalue bir referanstan ziyade.

Aslında ilk dava doğruysa, ihtiyacımız bile yok forward daha fazla İşte evrensel referans parametrelerinin nasıl geçtiğini göstermek için bir deneme

template <typename T, typename U>
void g(T&& t, U&& u)
{
    std::cout << "t is lvalue ref: "
              << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
    std::cout << "t is rvalue ref: "
              << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
    std::cout << "u is lvalue ref: "
              << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 1
    std::cout << "u is rvalue ref: "
              << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 0
}

template <typename T, typename U>
void f(T&& t, U&& u)
{
    std::cout << "t is lvalue ref: "
              << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
    std::cout << "t is rvalue ref: "
              << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
    std::cout << "u is lvalue ref: "
              << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 0
    std::cout << "u is rvalue ref: "
              << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 1

    g(t, u);
}

int main()
{
    std::unique_ptr<int> t;
    f(t, std::unique_ptr<int>());
    return 0;
}

Program her ikisi de çıkıyor t ve u geçti f için g Buna rağmen lvalue referansları u rvalue bir referanstır f. Yani ilk durumda parametre forward rvalue referans olma şansı yoktur.

identity parametre türünü evrensel referanstan bir rvalue referansa değiştirmek için kullanılır (Redl tarafından belirtildiği gibi, kullanımı daha hassastır) std::remove_reference). Ancak bu değişiklik, şablon tipi kesintinin artık mümkün olmadığından, forward zorunlu, sonuç olarak yazacağız forward<T>(t).

Ama sorunuzdaki ikinci durum da, Redl tarafından da belirtildiği gibi doğru değildir, doğru yaklaşım, parametresi bir değer referansı olan bir aşırı yüktür.

Bulabildiğim en basit uygulama şudur:

template <typename T>
T&& forward(typename identity<T>::type& param)
{
    return static_cast<T&&>(param);
}

Örneğin, evrensel referanslar için çalışır

template <typename T, typename U>
void f(T&& t, U&& u)
{
    ::forward<T>(t);
    ::forward<U>(u);
}

std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
// deduction in f:
//   T = unique_ptr&, decltype(t) = unique_ptr&
//   U = unique_ptr, decltype(u) = unique_ptr&& (but treated as an lvalue reference)
// specialization of forward:
//   forward<T> = forward<unique_ptr&>, param type = unique_ptr&
//                                      return type = unique_ptr&
//   forward<U> = forward<unique_ptr>,  param type = unique_ptr&
//                                      return type = unique_ptr&&

3
2017-07-11 02:04