2015-06-12 12 views
5

Bazı callable'ların ilk parametresi olarak bir referans referans alıp almadığını anlatan bir özellik yazmaya çalışıyorum. Bu, bazı meta programlamanın, callable'ın harici kod tarafından sağlandığı satılabilirliği çağırırken (veya bir kullanıcı tarafından sağlanan callable tipte aşırı yüklenme), semantiklerin taşıma veya kopyalamanın kullanılıp kullanılmayacağını ayarlar.Bazı callable'ların bir referans referans alıp almadığı nasıl belirlenir?

#include <functional> 
#include <iostream> 
#include <type_traits> 

// Does the callable when called with Arg move? 
template<class F, class Arg> struct is_callable_moving 
{ 
    typedef typename std::decay<Arg>::type arg_type; 
    typedef typename std::function<F(arg_type)>::argument_type parameter_type; 
    static constexpr bool value = std::is_rvalue_reference<parameter_type>::value; 
}; 

int main(void) 
{ 
    auto normal = [](auto) {}; // Takes an unconstrained input. 
    auto moving = [](auto&&) {}; // Takes a constrained to rvalue ref input. 
    std::cout << "normal=" << is_callable_moving<decltype(normal), int>::value << std::endl; 
    std::cout << "moving=" << is_callable_moving<decltype(moving), int>::value << std::endl; // should be 1, but isn't 
    getchar(); 
    return 0; 
} 

yukarıdaki açıkçası çalışmıyor ama umarım aradığım şeyi açıklıyor: Yalnızca bir rvalue referans olmasının onların parametresini kısıtlayan callables algılamak istiyorum.

Get lambda parameter type gibi diğer Yığın Taşması yanıtlarının burada yararlı olmadığına dikkat edin çünkü C++ 14 genel lambdalarını (yani, otomatik parametreleri alan) desteklemem gerekiyor ve bu nedenle de telefon operatörü lambda'nın adresini alarak temel almayı tipi aşırı yükü giderememekle başarısız olur.

is_callable_working'in bir Arg türü aldığını ve callable F'nin doğru aşırı yüklenmesinin F(Arg) aracılığıyla bulunacağını unutmayın. Ben algılamak istiyorum şey F(Arg) için kullanılabilir aşırı bir F::operator()(Arg &&) veya Arg>) için F::operator()( < başka başvuru türü olup olmadığıdır. F() için belirsiz aşırı yükler mevcutsa, örn. hem F(Arg) hem de F(Arg &&) sonra derleyici hata yapar, ancak [](auto)[](auto &&) arasında belirsiz olmamalıdır.

Düzenleme: umarım sorumu açıklığa kavuşturuldu. C++ meta programlamanın argümanlar üzerindeki kısıtlamaları tespit edip edemeyeceğini gerçekten soruyorum.

Düzenleme 2: Burada bazı açıklama olduğunu.

template<class T> class monad 
{ 
    ... 
    template<class U> monad<...> bind(U &&v); 
}; 

monad<T>.bind([](T}{}) kopya ile T alır ve ben monad<T>.bind([](T &&){}) rvalue referans olarak T alır istiyorum (yani çağrılabilir ondan hareket olabilir): My tam kullanım durumunda budur.

Yukarıda da belirtildiği gibi, monad<T>.bind([](auto){})'un kopyalayarak T'yi almasını ve monad<T>.bind([](auto &&){})'un r değerini referans alarak almasını istiyorum.

I belirtildiği gibi

bu, farklı etkiler belirtilen nasıl çağrılabilir bağlı olarak oluşan ve böylece monad<T>.bind() arasında aşırı bir tür. Eğer birisi lambdalardan önce olabileceğimiz gibi, çağrı imzasına dayalı olarak bind()'u aşırı yükleyebilseydi, tüm bunlar kolay olurdu. Burada sorun olan lambda tiplerini yakalamanın bilinmemesi ile uğraşıyor.

+1

Her iki sonucu da sağlayan aşırı yüklü satırlar için ne tür bir sonuç beklersiniz? – Quentin

+0

@Quentin: is_callable_moving içindeki ikinci parametrenin, doğru aşırı yüklenmeye sahip olması ve/veya otomatik lambda'nın doğru bir şekilde uygulanabilmesi için çağrılabilir olanı çağırmak için kullanılan argüman olduğunu not edebilirsiniz.Umut, geçici olarak (otomobili) bir şekilde callable (auto &&) 'den ayırt edilebilir olurdu. –

+0

Satılabilirlik yalnızca bir argüman alır mı? –

cevap

7

Bu en aklı başında lambdas (ve dolaylı olarak, lambdas gibi yeterince olan şeyler) için çalışması gerekir: const auto&& veya volatile gibi hiçbir çılgın şeyler =

struct template_rref {}; 
struct template_lref {}; 
struct template_val {}; 

struct normal_rref{}; 
struct normal_lref{}; 
struct normal_val{}; 

template<int R> struct rank : rank<R-1> { static_assert(R > 0, ""); }; 
template<> struct rank<0> {}; 

template<class F, class A> 
struct first_arg { 

    using return_type = decltype(std::declval<F>()(std::declval<A>())); 
    using arg_type = std::decay_t<A>; 


    static template_rref test(return_type (F::*)(arg_type&&), rank<5>); 
    static template_lref test(return_type (F::*)(arg_type&), rank<4>); 
    static template_lref test(return_type (F::*)(const arg_type&), rank<3>); 
    static template_val test(return_type (F::*)(arg_type), rank<6>); 

    static template_rref test(return_type (F::*)(arg_type&&) const, rank<5>); 
    static template_lref test(return_type (F::*)(arg_type&) const, rank<4>); 
    static template_lref test(return_type (F::*)(const arg_type&) const, rank<3>); 
    static template_val test(return_type (F::*)(arg_type) const, rank<6>); 

    template<class T> 
    static normal_rref test(return_type (F::*)(T&&), rank<12>); 
    template<class T> 
    static normal_lref test(return_type (F::*)(T&), rank<11>); 
    template<class T> 
    static normal_val test(return_type (F::*)(T), rank<10>); 

    template<class T> 
    static normal_rref test(return_type (F::*)(T&&) const, rank<12>); 
    template<class T> 
    static normal_lref test(return_type (F::*)(T&) const, rank<11>); 
    template<class T> 
    static normal_val test(return_type (F::*)(T) const, rank<10>); 

    using result = decltype(test(&F::operator(), rank<20>())); 
}; 

"aklı başında". En yüksek rütbe ile canlı işlev seçilir -

rank

aşırı yük çözünürlüğünü yönetmenize yardımcı olmak için kullanılmaktadır.

İlk fonksiyon şablonları olan yüksek sıralarda test aşırı yüklenmeleri düşünün.F::operator() bir şablon ise, ilk argüman çıkarılmayan bir bağlamdır ([temp.deduct.call] /p6.1 ile) ve T çıkarılabilir ve aşırı yüklenme çözünürlüğünden kaldırılır.

F::operator() bir şablon değilse, kesinti gerçekleştirilir, uygun aşırı yük seçilir ve ilk parametrenin türü işlevin dönüş türünde kodlanır. rütbeler etkin biçimde if-else-if ilişki kurmak: İlk bağımsız değişken bir rvalue referans ise

  • , kesinti iki sıra 12 aşırı biri için başarılı olur, bu yüzden seçti; Aksi takdirde, 12 aşırı yükleme için kesinti başarısız olur. İlk argüman bir lvalue referansı ise, kesinti 11. derece aşırı yüklenmelerden biri için başarılı olacak ve bu da seçilecektir; Aksi halde, ilk argüman değerdir ve kesinti, 10. sırada aşırı yüklenme için başarılı olacaktır. Bir referans türü olarak T çıkarabiliriz - kesinti bağımsız olarak her zaman birinci argüman doğasının o biri için başarılı olacaktır çünkü biz geçen rütbe 10 terk

Not. (Aslında, altı şablon aşırı yükünün, kısmi sıralama kurallarından dolayı aynı sıralamaya sahip olsaydık doğru sonucu alırdık, ancak IMO, bu şekilde anlaşılması daha kolay.)

Düşük dereceli test için İlk parametresi olarak sabit kodlanmış işaretçi-üye işlev türlerine sahip aşırı yüklenmeler. Bunlar sadece F::operator() bir şablon ise (eğer daha yüksek sıralı aşırı yükler geçerli değilse) gerçekten oyunda. Bir işlev şablonunun adresini bu işlevlere iletmek, parametre türüyle eşleşen bir işlev türü elde etmek için bu işlev şablonu için şablon bağımsız değişkenin gerçekleştirilmesine neden olur (bkz. [Over.over]).

[](auto){}, [](auto&){}, [](const auto&){} ve [](auto&&){} vakalarını dikkate alırız. şöyle saflarında kodlanmış mantıktır:

  • fonksiyon şablonu olmayan bir referans arg_type almaya örneği oluşturulmak, o zaman olmalıdır (auto) (sıra 6); İşlev şablonu arg_type&& rvalue referans türü alan bir öğeye örneklenebiliyorsa, (auto&&) olmalıdır (sıra 5);
  • İşlev şablonu, yapı-dışı kalifiye olmayan bir arg_type& alarak bir şeye örneklenebilirse, (auto&) olmalıdır (sıra 4); İşlev şablonu const arg_type& alan bir şeye örneklenebilirse, (const auto&) (sıra 3) olmalıdır. aksi takdirde de diğer üç imzaları biçimleme örneğini çünkü burada

, yine, ilk (auto) olaya el. Üstelik, (auto&&) kasasını, (auto&) durumundan önce ele alıyoruz, çünkü bu kesinti için yönlendirme referans kuralları geçerlidir ve auto&&, arg_type&'dan çıkarılabilir.

+0

Ayrıca, sıralamanın nasıl ve neden işlediğini açıklamanız gerektiğini düşünüyorum ve daha sonra gerçekten çok iyi bir cevabımız olacak. Ve çok teşekkürler T.C., yardım için dokümanlar için size para vereceğim ve önümüzdeki hafta hafif gelecek vaad eden vaatlerde ilk adım olarak boost-dev'de duyurulan bu monayı görmelisiniz. Topluluğun nasıl hoşlandığını göreceğiz ya da görmeyeceğiz. –

+0

@NiallDouglas Bazı testler ekledim ve "sınama" nın biraz sıcağa uğramış aşırı yüklenmelerinin sıralamasını değiştirdim (ayrıca "enable_if" den kurtulmaktayız.) –

+0

İyileştirme için teşekkürler. Cevabınızı görebilirsiniz. umarız daha az kırılgan bir şey, umarız ki yakında gelecek olan Boost monad , https://github.com/ned14/boost.spinlock/blob/master/include/boost/spinlock/monad.hpp#L88 ilgilenenler için. sınıftaki dokümanlardaki onaylar. –