ทำไมฉันไม่สามารถดึงข้อมูลดัชนีของตัวแปรและใช้เพื่อรับเนื้อหาได้


10

ฉันพยายามเข้าถึงเนื้อหาของตัวแปร ฉันไม่รู้ว่ามีอะไรในนั้น แต่โชคดีที่ตัวแปรนี้ทำ ดังนั้นฉันคิดว่าฉันจะถามตัวแปรว่าดัชนีนั้นเปิดอยู่จากนั้นใช้ดัชนีนั้นกับstd::getเนื้อหา

แต่นี่ไม่ได้รวบรวม:

#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;
}

ข้อผิดพลาดเกิดขึ้นในการstd::getโทร:

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();
               ^~~

ฉันจะแก้ไขสิ่งนี้ได้อย่างไร


3
ฉันสงสัยว่าข้อผิดพลาดที่คุณได้รับนั้นเกี่ยวข้องกับดัชนีไม่ใช่การแสดงออกอย่างต่อเนื่อง กรุณาโพสต์ข้อความผิดพลาดคอมไพเลอร์เพื่อให้เราสามารถให้ความช่วยเหลือที่มีความหมาย
patatahooligan

ไม่มี constexpr
Rlyeh

อ๊ะ! คุณพูดถึงข้อผิดพลาด แต่คุณไม่ได้โพสต์ข้อความที่ผิดพลาด
โจนาธานวู้ด

1
ขออภัยในความผิดพลาดฉันได้อัปเดตคำถาม
Alex

คำตอบ:


4

เป็นหลักคุณไม่สามารถ

คุณเขียน:

ฉันไม่รู้ว่ามีอะไรในนั้น แต่โชคดีที่ตัวแปรนี้ทำ

... แต่ ณ รันไทม์เท่านั้นไม่ใช่เวลาคอมไพล์
และนั่นหมายความว่าidxคุณค่าของคุณไม่ได้รวบรวมเวลา
และนั่นหมายความว่าคุณไม่สามารถใช้งานget<idx>()ได้โดยตรง

สิ่งที่คุณสามารถทำได้คือมีคำสั่งเปลี่ยน; น่าเกลียด แต่มันจะได้ผล:

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.
}

อย่างไรก็ตามมันค่อนข้างน่าเกลียด ตามความเห็นที่แนะนำคุณอาจใช้std::visit()(ซึ่งไม่แตกต่างจากโค้ดด้านบนมากนักยกเว้นการใช้อาร์กิวเมนต์เท็มเพลตแบบ Variadic แทนที่จะเป็นการชัดเจน) และหลีกเลี่ยงการสลับทั้งหมด สำหรับวิธีการอิงดัชนีอื่น ๆ (ไม่เฉพาะเจาะจงstd::variant) โปรดดู:

สำนวนการจำลองพารามิเตอร์เทมเพลตเป็นตัวเลข?


@Caleth: ใช่ แก้ไข
einpoklum

5

คอมไพเลอร์จำเป็นต้องรู้คุณค่าของidxเวลาในการรวบรวมstd::get<idx>()เพราะมันถูกใช้เป็นอาร์กิวเมนต์เท็มเพลต

ตัวเลือกแรก: ถ้ารหัสตั้งใจให้รันในเวลารวบรวมให้ทำทุกอย่างconstexpr:

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

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

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

งานนี้เพราะstd::variantเป็นconstexprมิตร (ผู้สร้างและวิธีการทั้งหมดconstexpr)

ตัวเลือกที่สอง: หากรหัสไม่ได้หมายถึงการรันในเวลารวบรวมซึ่งน่าจะเป็นกรณีตัวแปลไม่สามารถอนุมาน ณ เวลารวบรวมชนิดของresเพราะมันอาจเป็นสามสิ่งที่แตกต่างกัน ( int,floatหรือchar) C ++ เป็นภาษาที่พิมพ์แบบสแตติกและคอมไพเลอร์จะต้องสามารถอนุมานประเภทของauto res = ...นิพจน์ที่ตามมา (เช่นต้องเป็นประเภทเดียวกันเสมอ)

คุณสามารถใช้std::get<T>กับชนิดแทนที่จะเป็นดัชนีถ้าคุณรู้อยู่แล้วว่ามันจะเป็นอะไร:

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

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

โดยทั่วไปใช้std::holds_alternativeเพื่อตรวจสอบว่าชุดตัวเลือกนั้นมีประเภทที่กำหนดไว้หรือไม่และจัดการแยกกัน:

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&
    // ...
}

หรือคุณสามารถใช้ std::visitหรือคุณสามารถใช้นี่ซับซ้อนกว่าเล็กน้อย: คุณสามารถใช้แลมบ์ดา / เทมเพลตฟังก์ชันที่เป็นแบบไม่เชื่อเรื่องพระเจ้าและทำงานได้กับทุกประเภทของตัวแปรหรือส่ง functor ผ่านโอเปอเรเตอร์การโทรที่มากเกินไป:

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);

ดูstd :: Visitสำหรับรายละเอียดและตัวอย่าง


3

ปัญหาคือstd::get<idx>(var);ต้องการ (สำหรับidxมีการรวบรวมค่าที่รู้จัก ) เวลา

ดังนั้นconstexprค่า

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

แต่เพื่อเริ่มต้นidxเป็นconstexprยังvarต้องเป็นconstexpr

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

…และตัวแปร constexpr นั้นไม่แตกต่างกันมาก
Davis Herring

@DavisHerring - นั่นก็เป็นจริงเช่นกัน
สูงสุด 66

2

ปัญหาเกิดขึ้นจากแม่แบบที่มีการสร้างอินสแตนซ์ในเวลาคอมไพล์ขณะที่ดัชนีที่คุณได้รับนั้นถูกคำนวณ ณ เวลาทำการ ในทำนองเดียวกันชนิดของ C ++ จะถูกกำหนดในเวลาคอมไพล์ดังนั้นแม้จะมีการautoประกาศresจะต้องมีชนิดที่เป็นรูปธรรมสำหรับโปรแกรมที่จะมีรูปแบบที่ดี ซึ่งหมายความว่าแม้ไม่มีข้อ จำกัด ในแม่แบบสิ่งที่คุณพยายามทำนั้นเป็นไปไม่ได้โดยธรรมชาติสำหรับการแสดงออกที่ไม่คงที่std::variantที่ เราจะทำงานนี้ได้อย่างไร

ประการแรกหากตัวแปรของคุณเป็นนิพจน์คงที่แน่นอนรหัสจะรวบรวมและทำงานตามที่คาดไว้

#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;
}

มิฉะนั้นคุณจะต้องใช้กลไกการแตกแขนงแบบแมนนวล

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

คุณสามารถกำหนดสาขาเหล่านี้โดยใช้รูปแบบของผู้เข้าชมให้ดูมาตรฐาน :: เข้าชม


1

สิ่งนี้เป็นไปไม่ได้โดยเนื้อแท้ในรูปแบบของ C ++ พิจารณา

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

ซึ่งfจะถูกเรียกว่าf<int>หรือf<double>? หากเป็น "ทั้งคู่" หมายความว่าgมีสาขา (ซึ่งไม่มี) หรือมีสองเวอร์ชันg(ซึ่งเพิ่งผลักปัญหาไปยังผู้โทร) และคิดว่าf(T,U,V,W)คอมไพเลอร์หยุดที่ใด?

จริงๆแล้วมีข้อเสนอสำหรับ JIT สำหรับ C ++ ที่จะอนุญาตสิ่งต่าง ๆ เช่นนี้โดยการรวบรวมรุ่นเพิ่มเติมเหล่านั้นfเมื่อพวกเขาถูกเรียก แต่มันเร็วมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.