Neden bir varyantın dizinini alamıyorum ve içeriğini almak için kullanamıyorum?


10

Bir varyantın içeriğine erişmeye çalışıyorum. Orada ne olduğunu bilmiyorum, ama neyse ki varyant yapıyor. Bu yüzden değişkene sadece hangi indekste olduğunu soracağımı ve bu indeksi std::getiçeriğine kullanacağımı düşündüm .

Ancak bu derlenmez:

#include <variant>

int main()
{
  std::variant<int, float, char> var { 42.0F };

  const std::size_t idx = var.index();

  auto res = std::get<idx>(var);

  return 0;
}

Hata std::getçağrıda olur :

error: no matching function for call to get<idx>(std::variant<int, float, char>&)’
   auto res = std::get<idx>(var);
                               ^
In file included from /usr/include/c++/8/variant:37,
                 from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
     get(std::pair<_Tp1, _Tp2>& __in) noexcept
     ^~~
/usr/include/c++/8/utility:216:5: note:   template argument deduction/substitution failed:
main.cpp:9:31: error: the value of idx is not usable in a constant expression
   auto res = std::get<idx>(var);
                               ^
main.cpp:7:15: note: std::size_t idx is not const
   std::size_t idx = var.index();
               ^~~

Bunu nasıl düzeltebilirim?


3
Aldığınız hatanın dizinin sabit bir ifade olmamasıyla ilgili olduğundan şüpheleniyorum. Anlamlı yardım sağlayabilmemiz için lütfen derleyici hata mesajlarını gönderin.
patatahooligan

İçerik eksik mi?
Rlyeh

Tüh! Bir hata hakkında konuştunuz, ancak hatanın tam metnini göndermediniz.
Jonathan Wood

1
İhmal için üzgünüm, soruyu güncelledim
Alex

Yanıtlar:


4

Aslında yapamazsınız.

Sen yazdın:

Orada ne olduğunu bilmiyorum ama neyse ki varyant

... ama sadece çalışma zamanında, derleme zamanında değil.
Ve bu, idxdeğerinizin derleme zamanı olmadığı anlamına gelir .
Ve bu araçlar size kullanamazsınız get<idx>()doğrudan.

Yapabileceğiniz bir şey bir switch deyimidir; çirkin, ama işe yarayacaktı:

switch(idx) {
case 0: { /* code which knows at compile time that idx is 0 */ } break;
case 1: { /* code which knows at compile time that idx is 1 */ } break;
// etc. etc.
}

Ancak bu oldukça çirkin. Yorumların önerdiği gibi, std::visit()(bu açık olmak yerine varyasyon şablonu argümanlarını kullanmak dışında, yukarıdaki koddan çok farklı değildir) ve geçişi tamamen önleyebilirsiniz. Diğer indeks tabanlı yaklaşımlar (spesifik olmayan std::variant) için bakınız:

Çalışma zamanı sayısal şablon parametrelerini simüle etmek için deyim?


@Caleth: Evet. Düzenlenen.
einpoklum

5

Derleyicinin çalışması idxiçin derleme zamanında değerini bilmesi gerekir std::get<idx>(), çünkü bir şablon argümanı olarak kullanılır.

İlk seçenek: Kodun derleme zamanında çalışması gerekiyorsa, her şeyi yapın constexpr:

constexpr std::variant<int, float, char> var { 42.0f };

constexpr std::size_t idx = var.index();

constexpr auto res = std::get<idx>(var);

Bu dostça olduğu std::variantiçin işe yarıyor constexpr(kurucuları ve yöntemleri hepsi constexpr).

İkinci seçenek: Kod olasılıkla uygulamalarda olduğu gibi derleme zamanda çalıştırmak anlamına gelmez, derleyici derleme zamanında türünü anlamak edemez resüç farklı şeyler (olabileceğinden, int, floatveya char). C ++ statik olarak yazılan bir dildir ve derleyici aşağıdaki ifadeden türünü çıkarabilmelidir auto res = ...(yani her zaman aynı tür olmalıdır).

Ne std::get<T>olacağını zaten biliyorsanız, bir dizin yerine türle kullanabilirsiniz :

std::variant<int, float, char> var { 42.0f }; // chooses float

auto res = std::get<float>(var);

Genel olarak, std::holds_alternativevaryantın verilen türlerin her birini tutup tutmadığını kontrol etmek için kullanın ve ayrı ayrı kullanın:

std::variant<int, float, char> var { 42.0f };

if (std::holds_alternative<int>(var)) {
    auto int_res = std::get<int>(var); // int&
    // ...
} else if (std::holds_alternative<float>(var)) {
    auto float_res = std::get<float>(var); // float&
    // ...
} else {
    auto char_res = std::get<char>(var); // char&
    // ...
}

Alternatif olarak kullanabilirsiniz std::visit. Bu biraz daha karmaşıktır: tip-agnostik olan ve varyantın tüm tipleri için çalışan bir lambda / templated fonksiyonu kullanabilirsiniz veya aşırı yüklenmiş bir çağrı operatörü ile bir functor iletebilirsiniz:

std::variant<int, float, char> var { 42.0f };

std::size_t idx = var.index();

std::visit([](auto&& val) {
    // use val, which may be int&, float& or char&
}, var);

Ayrıntılar ve örnekler için bkz. Std :: visit .


3

Sorun, derleme zamanı bilinen bir değer std::get<idx>(var);gerektiren (için idx) olmasıdır.

Yani bir constexprdeğer

// VVVVVVVVV
   constexpr std::size_t idx = var.index();

Ama başlatmak için idxolduğu gibi constexpr, aynı zamanda varolmak zorundaconstexpr

// VVVVVVVVV
   constexpr std::variant<int, float, char> var { 42.0F };

… Ve bir constexpr varyantı çok varyant değil.
Davis Herring

@DavisHerring - Bu da doğrudur.
max66

2

Sorun, elde ettiğiniz dizin çalışma zamanında hesaplanırken derleme zamanında örnekleme şablonlarından kaynaklanır. Benzer şekilde, C ++ türleri de derleme zamanında tanımlanır, bu nedenle autobildirimle bile res, programın iyi biçimlendirilmesi için somut bir türe sahip olması gerekir. Bu, şablonda kısıtlama olmasa bile, yapmaya çalıştığınız şeyin sabit olmayan ifadeler için doğal olarak imkansız olduğu anlamına gelir std::variant. Biri bu sorunu nasıl çözer?

İlk olarak, varyantınız gerçekten sabit bir ifade ise, kod beklendiği gibi derlenir ve çalışır

#include <variant>

int main()
{
  constexpr std::variant<int, float, char> var { 42.0f };

  constexpr std::size_t idx = var.index();

  auto res = std::get<idx>(var);

  return 0;
}

Aksi takdirde bazı manuel dallanma mekanizmaları kullanmanız gerekecektir.

if (idx == 0) {
    // Now 'auto' will have a concrete type which I've explicitly used
    int value == std::get<0>(var);
}

Bu dalları ziyaretçi desenini kullanarak tanımlayabilirsiniz, bkz. Std :: visit .


1

Bu, C ++ modelinde doğal olarak imkansızdır; düşünmek

template<class T> void f(T);
void g(std::variant<int,double> v) {
  auto x=std::get<v.index()>(v);
  f(x);
}

Hangisi fçağrılıyor, f<int>ya f<double>? "Her ikisi de" ise, bunun gbir dal içerdiği (ki içermediği) veya iki sürümü olduğu anlamına gelir g(bu sadece sorunu arayan kişiye iletir). Ve düşünün — f(T,U,V,W)derleyici nerede duruyor?

Aslında C ++ için bir JIT için çağrıldığında bu ek sürümleri derleyerek böyle şeylere izin verecek bir teklif var f, ama çok erken.

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.