Neden std :: swap, C ++ 20'den önce constexpr olarak işaretlenmiyor?


14

C ++ 20'de std::swapbir constexprişlev haline gelir .

Standart kütüphanenin constexpr, şeyleri işaretlemede dilin gerçekten gerisinde olduğunu biliyorum , ancak 2017 <algorithm>yılına kadar, bir sürü başka şey gibi oldukça karmaşıktı. Henüz - std::swapdeğildi. Bu işaretlemeyi engelleyen garip bir dil kusuru olduğunu belirsiz bir şekilde hatırlıyorum, ancak detayları unutuyorum.

Birisi bunu kısa ve öz bir şekilde açıklayabilir mi?

Motivasyon: C ++ 11 / C ++ 14 kodunda benzer bir std::swap()işlevi işaretlemenin neden kötü bir fikir olabileceğini anlamalıyız constexpr.

Yanıtlar:


11

Garip dil ​​sorunu CWG 1581 :

Madde 15 [özel], özel üye işlevlerinin yalnızca odr kullanıldıklarında dolaylı olarak tanımlandıklarını açıkça göstermektedir. Bu, değerlendirilmemiş bağlamlarda sabit ifadeler için bir sorun yaratır:

struct duration {
  constexpr duration() {}
  constexpr operator int() const { return 0; }
};

// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});

Buradaki sorun constexpr duration::duration(duration&&), bu programda örtülü olarak tanımlanmasına izin verilmemesidir , bu nedenle başlatıcı listesindeki ifade sabit bir ifade değildir (tanımlanmamış bir constexpr işlevini çağırdığı için), bu nedenle hazır başlatıcı daraltma dönüşümü içerir , böylece program kötü biçimlendirilmiş.

1 numaralı satırı kaldırırsak, move yapıcısı dolaylı olarak tanımlanır ve program geçerlidir. Uzaktan bu ürkütücü eylem son derece talihsiz. Uygulamalar bu noktada birbirinden ayrılmaktadır.

Sorun açıklamasının geri kalanını okuyabilirsiniz.

Bu sorun için bir çözüm , 2017'de Albuquerque'deki P0859'da (C ++ 17 gönderildikten sonra) kabul edildi. Bu sorun, bir engelleyici olduğunu hem olması mümkün constexpr std::swap(giderilmiş P0879 ) ve constexpr std::invoke(giderilmiş P1065 20 C ++, her ikisi de, aynı zamanda CWG1581 örnekler vardır).


Burada anlaşılması en kolay örnek, bence, P1065'te belirtilen LLVM hata raporunun kodudur:

template<typename T>
int f(T x)
{
    return x.get();
}

template<typename T>
constexpr int g(T x)
{
    return x.get();
}

int main() {

  // O.K. The body of `f' is not required.
  decltype(f(0)) a;

  // Seems to instantiate the body of `g'
  // and results in an error.
  decltype(g(0)) b;

  return 0;
}

CWG1581 ilgili olduğunu ne zaman kullanıldığında sadece tanımlanmış olduğunuzu constexpr üye fonksiyonları tanımlanır ve çözünürlük olmasını sağlar. P0859 sonra yukarıdaki (tipini iyi oluşturulur bolan int).

Yana std::swapve std::invokeher iki üye fonksiyonları için (eski içinde hareket inşaat / atama ve çağrı operatörü / ikincisinde vekil çağrıları), ikisi de bu sorunun çözümü bağlıydı kontrol dayanmak zorundadır.


Öyleyse, CWG-1581 neden bir takas işlevinin bağlam olarak işaretlenmesini istenmeyen / engelliyor?
einpoklum

3
@einpoklum takas gerektirir std::is_move_constructible_v<T> && std::is_move_assignable_v<T>olup true. Özel üye işlevleri henüz oluşturulmadıysa bu gerçekleşemez.
NathanOliver

@ NathanOliver: Bunu cevabıma ekledim.
einpoklum

5

Sebep

(@NathanOliver nedeniyle)

Bir constexprtakas işlevine izin vermek için , - bu işlev için şablonu somutlaştırmadan önce - değiştirilen türün hareket ettirilebilir ve hareket atama özelliğine sahip olduğunu kontrol etmeniz gerekir. Ne yazık ki, sadece C ++ 20 çözülmesi bir dil hatası nedeniyle, sen yapamazsın ilgili üye fonksiyonları kadarıyla derleyici söz konusu olduğunda, henüz tanımlanmış olmayabilir çünkü bunun için kontrol edin.

Kronoloji

  • 2016: Antony Polukhin tüm fonksiyonları işaretlemek için P0202 önerisini sundu<algorithm>constexpr .
  • Standart komitenin ana çalışma grubu, kusur CWG-1581'i tartışıyor . Bu sorun, yukarıdaki açıklamaya sahip olmayı constexpr std::swap()ve ayrıca constexpr std::invoke()bkz.
  • 2017: Antony , teklifini ve diğer bazı yapıları dışlamak için birkaç kez revize etti std::swapve bu C ++ 17'ye kabul edildi.
  • 2017: CWG-1581 sorunu için bir çözüm P0859 olarak sunulur ve 2017 yılında standart komite tarafından kabul edilir (ancak C ++ 17 gönderildikten sonra).
  • 2017'nin sonu: Antony , CWG-1581'in kararından sonra constexpr yapmak için tamamlayıcı bir teklif olan P0879'ustd::swap() sundu.
  • 2018: Tamamlayıcı teklif C ++ 20'ye kabul edildi (?). Barry'nin işaret ettiği gibi, sabit std::invoke()düzeltme de öyle.

Özel durumunuz

Sen kullanabilirsiniz constexpreğer takas yok hareket-constructibility ve hareket-DEVRETME kontrol değil, doğrudan doğruya özellikle olmasını sağlar türlerinden bazıları başka bir özellik için kontrol edin. örneğin sadece ilkel türler ve sınıflar veya yapılar yok. Veya, teorik olarak, kontrollerden vazgeçebilir ve sadece karşılaşabileceğiniz herhangi bir derleme hatasıyla ve derleyiciler arasındaki kesintili davranışlarla başa çıkabilirsiniz. Her durumda, böyle std::swap()bir şeyle değiştirmeyin.

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.