Neden C ++, Foo <T> :: Foo (T&&) çağrısında T'yi çıkaramaz?


9

Aşağıdaki şablon yapısı göz önüne alındığında:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

Bu derlenir ve Tşu şekilde çıkarılır int:

auto f = Foo(2);

Ancak bu derlenmez: https://godbolt.org/z/hAA9TE

int x = 2;
auto f = Foo(x);

/*
<source>:12:15: error: no viable constructor or deduction guide for deduction of template arguments of 'Foo'
    auto f = Foo(x);
             ^

<source>:7:5: note: candidate function [with T = int] not viable: no known conversion from 'int' to 'int &&' for 1st argument
    Foo(T&&) {}
    ^
*/

Ancak Foo<int&>(x)kabul edilir.

Ama görünüşte gereksiz kullanıcı tanımlı bir kesinti kılavuzu eklediğimde, işe yarıyor:

template<typename T>
Foo(T&&) -> Foo<T>;

Kullanıcı tanımlı bir kesinti kılavuzu olmadan neden Tçıkartılamıyorsunuz int&?



Bu soruFoo<T<A>>
jtbandes

Yanıtlar:


5

Bence buradaki karışıklık ortaya çıkıyor çünkü yönlendirme referanslarıyla ilgili sentezlenmiş kesinti kılavuzları için özel bir istisna var.

Yapıcıdan oluşturulan sınıf şablonu argüman kesintisi ve kullanıcı tanımlı kesinti kılavuzundan oluşturulan işlev için aday işlevinin tam olarak aynı olduğu doğrudur, yani:

template<typename T>
auto f(T&&) -> Foo<T>;

ancak T&&kurucudan oluşturulan için basit bir değer referansı , kullanıcı tanımlı durumda yönlendirme referansıdır . Bu, C ++ 17 standardının [temp.deduct.call] / 3 ile belirtilir (taslak N4659, benimkini vurgula):

Bir yönlendirme referans bir ev-vasıfsız şablon parametresine bir rvalue referans bir sınıf şablonu bir şablon parametresi temsil etmez (sınıf şablon bağımsız değişken kesinti sırasında ([over.match.class.deduct])).

Bu nedenle, sınıf yapıcısından sentezlenen aday T, bir yönlendirme referansından (bir değer değeri referansı olduğu sonucuna Tvarılabilecek, bu yüzden T&&de bir değer değeri referansı gibi) çıkarılmayacak, bunun yerine sadece Treferans olarak değil T&&, her zaman bir rvalue referansı.


1
Açık ve özlü cevap için teşekkür ederim. Referans iletme kurallarında neden sınıf şablonu parametreleri için bir istisna olduğunu biliyor musunuz ?
jtbandes

2
@jtbandes Bu, ABD ulusal organı tarafından yapılan bir yorumun sonucu olarak değişmiş gibi görünüyor, p0512r0 makalesine bakınız . Gerçi yorumu bulamadım. Gerekliliğindeki Benim tahminim bir rvalue referans alarak bir kurucu yazarsanız genellikle bir belirtmek ister aynı şekilde çalışmasını bekliyoruz olmasıdır Foo<int>(...)sadece ya Foo(...)anlamak olabilir yönlendirme referansları olan durum (olmadığı, Foo<int&>. Bunun yerine
ceviz

6

Beri burada mesele, yani sınıf üzerinde şablonu olan Tyapıcı içinde, Foo(T&&)biz edilir değil tip kesinti yapmak; Her zaman bir r-değeri referansımız vardır. Yani, kurucu Fooaslında şöyle görünür:

Foo(int&&)

Foo(2)çünkü 2bir önkoşul.

Foo(x)çünkü xbağlanamayan bir değerdir int&&. std::move(x)Uygun türe ( demo ) atabilirsiniz.

Foo<int&>(x)yapıcı Foo(int&)referans daraltma kurallarından dolayı olur çünkü iyi çalışır ; başlangıçta standarda göre Foo((int&)&&)çöker Foo(int&).

"Yedekli" kesinti rehberinizle ilgili olarak: Başlangıçta temelde böyle bir yardımcı işlev gibi davranan kod için varsayılan bir şablon kesinti kılavuzu vardır:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
   return Foo<T>(std::move(value));
}

//... 
auto f = MakeFoo(x);

Bunun nedeni, standardın bu (kurgusal) şablon yönteminin sınıfla (Just T) aynı şablon parametrelerine ve ardından kurucu ile herhangi bir şablon parametresine sahip olmasını gerektirmesidir (bu durumda hiçbiri; yapıcı şablonlaştırılmaz). Ardından, işlev parametrelerinin türleri yapıcıdakilerle aynıdır. Bizim durumumuzda, somutlaştırıldıktan sonra Foo<int>, kurucu Foo(int&&)başka bir deyişle bir rvalue referansı gibi görünüyor . Dolayısıyla add_rvalue_reference_tyukarıdakilerin kullanımı .

Açıkçası bu işe yaramıyor.

"Yedekli" kesinti rehberinizi eklediğinizde:

template<typename T>
Foo(T&&) -> Foo<T>;

Sen ekli referans her türlü rağmen derleyici o ayırt izin Tyapıcı (içinde int&, const int&ya da int&&vb), sen referans (sadece sensiz olmak sınıfın bilgilerin tahmin türü amaçlanan T). Aniden Bunun nedeni vardır tip çıkarımlar gerçekleştirmek.

Şimdi şöyle görünen başka bir (kurgusal) yardımcı fonksiyon üretiyoruz:

template<class U>
Foo<U> MakeFoo(U&& u)
{
   return Foo<U>(std::forward<U>(u));
}

// ...
auto f = MakeFoo(x);

(Yapıcıya yaptığımız çağrılar, sınıf şablonu argümanı kesinti amacıyla yardımcı işlevine yönlendirilir, böylece Foo(x)olur MakeFoo(x)).

Bu veriyor U&&olmak int&ve Tbasitçe olmakint


İkinci şablonlama seviyesi gerekli görünmüyor; hangi değeri sağlıyor? Burada T &&'nin neden her zaman bir rvalue referansı olarak ele alındığını açıklayan bazı belgelere bağlantı verebilir misiniz ?
jtbandes

1
Fakat eğer T henüz çıkarılmamışsa, "T'ye dönüştürülebilir herhangi bir tür" ne demektir?
jtbandes

1
Geçici bir çözüm sunmak için hızlısınız, ancak bir şekilde değiştirmediğiniz sürece neden çalışmadığının açıklamasına daha fazla odaklanabilir misiniz? Neden olduğu gibi çalışmıyor? " xbağlanamayan bir değerdir " , int&&ancak anlamayan biri Foo<int&>(x)işe yarayabilecek, ancak otomatik olarak çözülmeyecek şaşkınlık duyacaktır - sanırım hepimiz neden daha derin bir anlayış istiyoruz.
Wyck

2
@Wyck: Nedenine daha fazla odaklanmak için yayını güncelledim.
AndyG

3
@Wyck Gerçek sebep çok basit: standart, sürprizleri önlemek için sentezlenmiş kesinti kılavuzlarında mükemmel yönlendirme semantiğini özellikle kapattı .
LF
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.