簡體   English   中英

為什么我不能檢索變體的索引並使用它來獲取其內容?

[英]Why can I not retrieve the index of a variant and use that to get its content?

我正在嘗試訪問變體的內容。 我不知道里面有什么,但幸運的是,這個變體確實如此。 所以我想我只會詢問變體它所在的索引,然后使用該索引來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();
               ^~~

我怎樣才能解決這個問題?

編譯器需要在編譯時知道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::variantconstexpr友好的(它的構造函數和方法都是constexpr )。

第二種選擇:如果代碼不打算在編譯時運行(很可能是這種情況),編譯器無法在編譯時推斷出res的類型,因為它可能是三種不同的東西( intfloatchar )。 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 這稍微復雜一點:您可以使用類型不可知且適用於所有變體類型的 lambda/模板化函數,或者傳遞帶有重載調用運算符的函子:

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

基本上,你不能。

你寫了:

我不知道里面有什么,但謝天謝地,變體確實如此

...但僅在運行時,而不是在編譯時。
這意味着您的idx值不是編譯時。
意味着您不能直接使用get<idx>()

你可以做的是有一個 switch 語句; 丑陋,但它會工作:

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() (這與上面的代碼沒有太大區別,除了使用可變參數模板參數而不是顯式)並完全避免切換。 對於其他基於索引的方法(不特定於std::variant ),請參閱:

用於模擬運行時數字模板參數的習語?

問題是std::get<idx>(var); 需要(對於idx )一個編譯時已知值。

所以一個constexpr

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

但是要將idx初始化為constexprvar也必須是constexpr

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

問題是由於模板在編譯時被實例化,而您獲得的索引是在運行時計算的。 類似地,C++ 類型也在編譯時定義,因此即使使用auto聲明, res必須有一個具體的類型才能使程序格式良好。 這意味着即使沒有對模板的限制,對於非常量表達式std::variant s,您嘗試做的事情本質上也是不可能的。 如何解決這個問題?

首先,如果您的變體確實是一個常量表達式,則代碼會編譯並按預期工作

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

您可以使用訪問者模式定義這些分支,請參閱std::visit

這在 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> 如果它是“both”,這意味着g包含一個分支(它不包含),或者有兩個版本的g (它只是將問題推給它的調用者)。 想想f(T,U,V,W) ——編譯器在哪里停止?

實際上有一個關於 C++ 的 JIT 的提議,它可以通過在調用f那些附加版本時編譯這些附加版本來允許這樣的事情,但現在還為時過早。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM