Decltype'in (auto) bazı kullanımları nelerdir?


151

C ++ 14'te decltype(auto)deyim tanıtılır.

Genellikle kullanımı, bildirimlerin verilen ifadedeki kuralları kullanmasına izin vermektirautodecltype .

Deyimin "iyi" kullanımına ilişkin örnekler aramak için sadece aşağıdaki gibi şeyleri ( Scott Meyers tarafından ), yani bir fonksiyonun dönüş tipi kesinti için düşünebilirim :

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

Bu yeni dil özelliğinin yararlı olduğu başka örnekler var mı?


2
Bu yazı temelde bunu kullanırken size derleyici optimizasyon için daha az seçenek sunacak çünkü bu deyim kaçınıyorum önermek stackoverflow.com/a/20092875/2485710
user2485710

Bir zamanlar decltype(auto)benzer bir şey için kullandım template<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }, ancak daha sonra return (p.first);şaşırtıcı bir şekilde işe yarar olduğumu fark ettim (ancak IIRC bu bile tasarlanmıştır).
dyp

@ user2485710 özellikle optimizasyon ile ilgili olduğundan emin değil, bir decltype(auto)şeyin kopyalanan / taşınan bir nesneye kopyalanmasına / beklenmesine neden olması durumunda kaza potansiyeli daha fazla .
underscore_d

Yanıtlar:


170

Genel kodda dönüş türü yönlendirme

Verdiğiniz ilk örnek gibi genel olmayan bir kod için, bir dönüş türü olarak referans almayı manuel olarak seçebilirsiniz:

auto const& Example(int const& i) 
{ 
    return i; 
}

ama genel kod yapabileceksiniz istiyorum mükemmel bir dönüş türü iletmek Eğer bir başvuru veya bir değerle ilgili olup olmadığını bilmeden. decltype(auto)size şu yeteneği verir:

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}

Özyinelemeli şablonlarda dönüş türü kesintisini geciktirme

Gelen bu Q & A şablonunun döndürme türü olarak belirtildi zaman bir kaç gün önce, şablon işlem yaptığı sonsuz yineleme karşılaşıldı decltype(iter(Int<i-1>{}))yerine decltype(auto).

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }

decltype(auto)burada şablon örnekleme tozu yerleştikten sonra dönüş türü kesintisini geciktirmek için kullanılır .

Diğer kullanımlar

decltype(auto)Diğer bağlamlarda da kullanabilirsiniz , örneğin N3936 Standardı taslağında ayrıca

7.1.6.4 otomatik spesifikasyon [dcl.spec.auto]

1 autove decltype(auto)tür belirticileri, daha sonra değiştirilecek bir yer tutucu türünü, bir başlatıcıdan çıkarılmasıyla veya izleyen dönüş türüyle birlikte açık bir belirtmeyle belirtir. autoTip-spesifik fi er de lambda jenerik lamda olduğunu belirtmek için kullanılır.

2 Yer tutucu türü , decl-specifier-seq, type-specifier-seq, dönüşüm-işlev-kimliği veya izleyen-dönüş-tipinde bir işlev bildiricisiyle, böyle bir bildiricinin geçerli olduğu herhangi bir bağlamda görünebilir . İşlev bildiriminde, işlevin bildirilen dönüş türünü belirten bir izleyen dönüş türü (8.3.5) varsa. İşlevin bildirilen dönüş türü bir yer tutucu türü içeriyorsa, işlevin dönüş türü, varsa, işlev gövdesindeki döndürme ifadelerinden çıkarılır.

Taslak ayrıca bu değişken başlatma örneğini de içerir:

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

17
(i)Vs farklı davranış iC ++ 14 yeni bir şey mi?
Danvil

14
@Danvil decltype(expr)ve decltype((expr))zaten C ++ 11 farklı, bu davranış genelleştirir.
TemplateRex

13
Bunu yeni öğrendim, korkunç bir tasarım kararı gibi geliyor ... parantezlerin sözdizimine dakik bir nüans ekleyerek.
Kahler

Bu iğrenmeyi her zaman isteyen örnek, tek satırlı dosyadan dizeye sözdizimidir (bu bağlantıda da belirtilmiştir). Her parçası geri görünüyor. Hiç bir belirsizlik beklemeyebilir ve bir örnekten gereksiz parantezleri zorunlu olarak kaldırabilirsiniz; Eğer SFINAE göre eleme işlemi ile Çözmek, belirsizlik beklenebilir ancak peşin (SF elimine edilmiştir bildiriminde dışındaki aday adayının olduğu AE); ve hayal kırıklığı içinde, keyfi parenslerin belirsizliği çözdüğünü düşünmeye başlar başlamaz devam edebilirsiniz, ancak bunu tanıttılar . Hayal ettiğim en çok CS101 profesörleri için can sıkıcı.
John P

@TemplateRex: Başvurulan soruda dönüş türü çözünürlüğünü geciktirme hakkında: Gördüğüm kadarıyla, belirli senaryoda , autosonuç yine de değere göre döndürüldüğünden, basit bir işi de yapardı ... Yoksa özledim mi? bir şey?
Aconcagua

36

Buradan alıntılar :

  • decltype(auto)öncelikle , türün çağırmakta olduğunuz bir ifadeyi tam olarak "izlemesini" istediğiniz yönlendirme işlevlerinin ve benzer sarmalayıcıların türünü çıkarmak için kullanışlıdır .

  • Örneğin, aşağıdaki işlevler göz önüne alındığında:


   string  lookup1();
   string& lookup2();

  • C ++ 11'de, dönüş türünün referansını korumayı hatırlayan aşağıdaki sarmalayıcı işlevlerini yazabiliriz:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • C ++ 14'te bunu otomatikleştirebiliriz:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

  • Bununla birlikte, decltype(auto)bunun ötesinde yaygın olarak kullanılan bir özellik olması amaçlanmamıştır.

  • Özellikle, yerel değişkenleri bildirmek için kullanılabilse de , bunu yapmak muhtemelen sadece bir antipatterndir, çünkü yerel bir değişkenin referansı, başlatma ifadesine bağlı olmamalıdır.

  • Ayrıca, return ifadesini nasıl yazdığınıza duyarlıdır.

  • Örneğin, aşağıdaki iki işlevin farklı dönüş türleri vardır:


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }

  • Birincisi string, ikincisi string&yerel değişkene bir referans olan döndürür str.

Gönderen önerisi daha amaçlanan kullanımları görebilirsiniz.


3
Neden sadece autoiade için kullanılmıyor ?
BЈовић

@ BЈовић genelleştirilmiş dönüş tipi kesintisi (yani autodönüş) ile de çalışabilir, ancak OP özellikle kullanımlarını istedi decltype(auto).
101010

3
Soru yine de konuyla ilgilidir. Geri dönüş türü ne olurdu auto lookup_a_string() { ... } ? Her zaman referans olmayan bir tür mü? Ve bu nedenle auto lookup_a_string() ->decltype(auto) { ... }referansların (bazı durumlarda) iade edilmesini sağlamak için zorlamak gerekir mi?
Aaron McDaid

@AaronMcDaid İndirgenebilir autodeğer şablonuna göre geçirme terimiyle tanımlanır, bu nedenle evet referans olamaz. Lütfen bekleyin auto, referans da dahil olmak üzere herhangi bir şey olabilir.
curiousguy

4
Bahsetmeye değer ek bir örnek, a öğesinin bir öğesini döndürmektir std::vector. Söyle template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }. Sonra S<bool>::operator[]uzmanlık nedeniyle sarkan bir referans dönecektir std::vector<bool>. Dönüş türünü değiştirmek decltype(auto)bu sorunu aşar.
Xoph
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.