변형의 색인을 검색 할 수없는 이유는 무엇입니까?


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

으악! 오류에 대해 이야기했지만 오류의 정확한 텍스트를 게시하지 않았습니다.
Jonathan Wood

1
생략해서 죄송합니다. 질문을 업데이트했습니다.
Alex

답변:


4

본질적으로, 당신은 할 수 없습니다.

당신은 썼습니다 :

나는 거기에 무엇이 있는지 모르지만 고맙게도 변형은

...하지만 컴파일 타임이 아닌 런타임에만.
그리고 그것은 당신의 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)은 다음을 참조하십시오.

런타임 숫자 템플릿 매개 변수를 시뮬레이션하기위한 관용구?


@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. 이것은 약간 더 복잡합니다. 유형에 구애받지 않고 모든 변형 유형에 작동하는 람다 / 템플릿 된 함수를 사용하거나 오버로드 된 호출 연산자로 펑터를 전달할 수 있습니다.

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

그러나 초기화 idxconstexprvar일했다constexpr

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

… 그리고 constexpr 변형은 그다지 변형되지 않습니다.
Davis Herring

@DavisHerring-사실이기도합니다.
max66

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

방문자 패턴을 사용하여 이러한 분기를 정의 할 수 있습니다 ( std :: visit 참조) .


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)컴파일러는 어디에서 멈추는가?

실제로 호출 될 때 추가 버전을 컴파일하여 이와 같은 것을 허용하는 C ++ 용 JIT에 대한 제안f있지만 매우 빠릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.