2016-07-14 30 views
5

std::for_each kabul eder ve değerine göre bir functor döndürür:Dönüş

funktoru taşındı ve dışarı hareket ettirilebilir rağmen
template< class InputIt, class UnaryFunction > 
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f); 

, ben hiç de buna dahil hiçbir nesne inşaat, olup olamayacağı ilgileniyorum. Böyle my_for_each kendi bildirirseniz:

template< class InputIt, class UnaryFunction > 
UnaryFunction&& my_for_each(InputIt first, InputIt last, UnaryFunction&& f); 

Ve my_for_each

, std::forward<UnaryFunction>(f)(...) ile f diyoruz, ben parametreyi inşa taşımak için maliyeti önlemek ve prim olarak ref-eleme saygı mümkün olabilir. Ama ne dönmem gerektiğinden emin değilim. Ben yaparsanız:

return std::forward<UnaryFunction>(f); 

kötü şeyler (örneğin sarkan referanslar) olabilir mi?

(I this post içinde for_each tasarlıyorum zaman bu soru gelir.)

+0

Dönüş türünüz UnaryFunction && bir referans değerdir. – Loreto

+0

@Loreto Hayır, argümanın değer kategorisine bağlıdır. –

+0

Oh, doğru, anlıyorum. – Loreto

cevap

8

referans ömrü uzatma gidip olmadığı için, tehlikeli aracılığıyla const& ve rvalue referans parametreler iletme kaydetti. Eğer T olduğu üzerinden bir yönlendirme referansı T&& geçirmek istediğinizde

doğru şey

dönmek için.Bu, bir geçici değeri geçici hale getirir ve referanslar olarak lvalues ​​bırakır.

Yani: Geçici f için (taşınan yapılır?) Kopyasını oluşturur

template< class InputIt, class UnaryFunction > 
UnaryFunction my_for_each(InputIt first, InputIt last, UnaryFunction&& f); 

.

Bu kopyayı saklarsanız, depoya seçilecektir, bu nedenle sıfır ek maliyet. Saklamıyorsanız ve taşıma pahalı değilse, bu temel olarak en uygunudur.

+0

Çıkarılan türü döndürmeyi seviyorum, çözümümle aynı net etkiye sahip ama tartışmalı olarak daha ergonomik; TIL. –

+0

Bir değerden geçtiğimde "UnaryFunction" kendisi bir değer referansı değil mi? Https://godbolt.org/g/6juQTS adresine bakın. –

+0

Seni takip etmiyorum. 'T' döndürme, bir kopyayı potansiyel olarak dayatır. – ThomasMcLeod

4

Kötü şeyler ve gerçekleşeceğini olabilir.

struct my_functor { 
    void operator()(int x) { sum += x; } 
    int sum = 0; 
} 

std::vector<int> v{1,2,3}; 

const auto& s = my_for_each(v.begin(), v.end(), my_functor{}); 
auto sum = s.sum; 

Bu kod tanımlanmamış bir davranış. Geçici bir my_functor örneği oluşturursunuz. Bu, işlev çağrısı my_for_each girdiğinde, şimdiki kullanım ömrünü uzatarak, geçerli referansa bağlanır. İşlev çıktığı zaman, geçici olarak bir rvalue başvurusu döndürür. fonksiyon çıktığında yerel bir işlev çünkü Ancak, rvalue referans bağlı geçici aslen, imha edilecek o. Bu noktada, geçici imha edilecek. Tüm bunların net etkisi, s'un hemen bir sarkan referans olmasıdır.

Orijinal for_each ile, funker'in değere göre döndürüleceğini ve referansın doğrudan ömrünü uzatarak referansa bağlı olacağını unutmayın.

Herb Sutter bu cppcon konuşma bu bahsediyor: https://www.youtube.com/watch?v=hEx5DNLWGgA. Temel olarak, bir işlevden bir referans/işaretçi döndürdüğünüzde, gerçekten nelere işaret edebileceğine dair çok az güvenli seçenek vardır. referans olarak almak Fonksiyon argümanlar bunlardan biri, ancak bunlar geçici çekmek ve işlevin dönüş sallanmakta neden çünkü const ref ve rvalue ref tarafından işlevleri argümanlar değildir. Bu konu hakkında yazdım: http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/. Bir functor alıp nesne inşaat önlemek istiyorsanız

, ben sadece referans ileterek alıp hiçbir şey dönecekti. Niye ya? Çünkü, eğer kullanıcı daha sonra functor'a ihtiyaç duymuyorsa, bir geri dönüşü geçebilir ve geri dönüşü yanlış kullanma konusunda endişelenmeyebilirler. Ve sonra ihtiyaç duyarlarsa, onu inşa edebilir, bir değer olarak geçebilir ve sonra kullanabilirler.

template< class InputIt, class UnaryFunction > 
void my_for_each(InputIt first, InputIt last, UnaryFunction&& f); 

my_functor s{}; 
my_for_each(v.begin(), v.end(), s); 
auto sum = s.sum; 

Yapı/yıkım yok, sorun yok. Her ne kadar performans nedenleriyle bunu engellemeye çalışıyorsanız, muhtemelen, functor/lambda'nızın büyük olmadığı, bu da alışılmadık bir durum olduğu için yanlış yönlendirildiğini söyleyebilirim. diğer ince cevap olarak

+0

İlginç. Evrensel referanslar söz konusu olduğunda, geçici olarak bağlanırsa, rvalue bir referans (bence) olmalı ve belki de buna göre dönüş türüne ('UnaryFunction &&' vs std :: decay_t ') karar verebiliriz. is_rvalue_reference :: value'? –

+0

@ZizhengTai Belki, ama bu zaten çok fazla karmaşıklık getiriyor. Aynı etkiyi elde etmek için çok daha basit bir yol ekledim. –

+0

* ... performans nedenleriyle bunu önlemek için çalışıyorsanız, muhtemelen yanlış yönlendiriliyor ... * - sadece açık olmak gerekirse, referansla geçmem gerekmesinin nedeni “my_for_each”, tuple benzeri nesneler üzerinde yinelemeli olarak çalışıyor (İlgilendiğiniz takdirde sorgumda bağlantılı gönderiye bakın), bu nedenle değerlerin aktarılması gereksiz kopyaların çoğunu oluşturabilir. –