variant_visit2
variant_visit2 (std::visit 함수)
이 페이지는 C++ 표준 라이브러리의 std::visit 함수에 대해 설명해요. std::visit은 std::variant 객체가 현재 보관 중인 값에 대해 주어진 방문자(visitor)를 호출하는 함수예요. 여러 variant를 동시에 방문할 수도 있고, C++20부터는 반환 타입을 명시할 수도 있어요.
출처: cppreference
본문
함수 선언
<variant> 헤더에 정의되어 있어요.
| 템플릿 선언 | (1) | (since C++17) |
|---|---|---|
template < class Visitor , class ... Variants > constexpr /* see below */ visit ( Visitor && v , Variants && ... values ); |
(1) | (since C++17) |
template < class R , class Visitor , class ... Variants > constexpr R visit ( Visitor && v , Variants && ... values ); |
(2) | (since C++20) |
헬퍼 템플릿:
| 템플릿 선언 | (3) | (설명 전용*) |
|---|---|---|
template < class ... Ts > auto && /*as-variant*/ ( std :: variant < Ts ... >& value ); |
(3) | (설명 전용*) |
template < class ... Ts > auto && /*as-variant*/ ( const std :: variant < Ts ... >& value ); |
(4) | (설명 전용*) |
template < class ... Ts > auto && /*as-variant*/ ( std :: variant < Ts ... >&& value ); |
(5) | (설명 전용*) |
template < class ... Ts > auto && /*as-variant*/ ( const std :: variant < Ts ... >&& value ); |
(6) | (설명 전용*) |
방문자 v( Variants의 타입들로부터 만들 수 있는 모든 조합으로 호출 가능한 Callable)를 Variants 값들에 적용해요.
VariantBases가 decltype(as-variant(std::forward<Variants>(values)))... (즉 sizeof...(Variants)개의 타입으로 이루어진 팩)일 때, 다음 식이 수행돼요.
INVOKE(std::forward<Visitor>(v), std::get<indices>(std::forward<VariantBases>(values))...),
그리고 C++20부터는:
INVOKE<R>(std::forward<Visitor>(v), std::get<indices>(std::forward<VariantBases>(values))...).
이 오버로드들은 VariantBases의 모든 타입이 유효한 타입일 때만 오버로드 해석에 참여해요. INVOKE 또는 INVOKE<R>(C++20부터)로 표시된 식이 유효하지 않거나, 서로 다른 indices에 대해 INVOKE 또는 INVOKE<R>(C++20부터)의 결과 타입이나 값 범주가 다르면 프로그램은 ill-formed예요.
매개변수
| v | - | Variants의 모든 variant에서 가능한 모든 대안을 받아들일 수 있는 Callable |
|---|---|---|
| values | - | 방문자에게 전달할 variant 목록 |
반환값
반환값은 방문자 호출 결과로 결정돼요.
예외
values에 있는 어떤 variant value_i에 대해 as-variant(value_i).valueless_by_exception()이 true이면 std::bad_variant_access를 던져요.
복잡도
variant의 개수가 0 또는 1이면 호출 가능 객체의 호출은 상수 시간에 구현돼요. 즉, variant에 저장될 수 있는 타입의 수에 의존하지 않아요. variant의 개수가 1보다 많으면 호출 가능 객체의 호출에 대한 복잡도 요구 사항은 없어요.
참고 사항
n을 (1 * ... * std::variant_size_v<std::remove_reference_t<VariantBases>>)라고 할 때, 구현은 보통 std::visit의 각 특수화에 대해 n개의 함수 포인터로 이루어진 (아마 다차원) 배열과 동등한 테이블을 생성해요. 이는 가상 함수의 구현과 비슷해요.
구현은 std::visit에 대해 n개의 분기를 가진 switch 문을 생성할 수도 있어요. (예를 들어 MSVC STL 구현은 n이 256보다 크지 않을 때 switch 문을 사용해요.)
일반적인 구현에서 v 호출의 시간 복잡도는 (아마 다차원) 배열의 요소에 접근하거나 switch 문을 실행하는 것과 같다고 볼 수 있어요.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_lib_variant |
202102L | (C++23) (DR17) | std::variant에서 파생된 클래스에 대한 std::visit |
예제
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
// the variant to visit
using value_t = std::variant<int, long, double, std::string>;
// helper type for the visitor #4
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
int main()
{
std::vector<value_t> vec = {10, 15l, 1.5, "hello"};
for (auto& v: vec)
{
// 1. void visitor, only called for side-effects (here, for I/O)
std::visit([](auto&& arg){ std::cout << arg; }, v);
// 2. value-returning visitor, demonstrates the idiom of returning another variant
value_t w = std::visit([](auto&& arg) -> value_t { return arg + arg; }, v);
// 3. type-matching visitor: a lambda that handles each type differently
std::cout << ". After doubling, variant holds ";
std::visit([](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "int with value " << arg << '\n';
else if constexpr (std::is_same_v<T, long>)
std::cout << "long with value " << arg << '\n';
else if constexpr (std::is_same_v<T, double>)
std::cout << "double with value " << arg << '\n';
else if constexpr (std::is_same_v<T, std::string>)
std::cout << "std::string with value " << std::quoted(arg) << '\n';
else
static_assert(false, "non-exhaustive visitor!");
}, w);
}
for (auto& v: vec)
{
// 4. another type-matching visitor: a class with 3 overloaded operator()'s
// Note: The `(auto arg)` template operator() will bind to `int` and `long`
// in this case, but in its absence the `(double arg)` operator()
// *will also* bind to `int` and `long` because both are implicitly
// convertible to double. When using this form, care has to be taken
// that implicit conversions are handled correctly.
std::visit(overloaded{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}, v);
}
}
출력:
10. After doubling, variant holds int with value 20
15. After doubling, variant holds long with value 30
1.5. After doubling, variant holds double with value 3
hello. After doubling, variant holds std::string with value "hellohello"
10 15 1.500000 "hello"
결함 보고서
다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 발표된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2970 | C++17 | 오버로드 (1)의 반환 타입이 INVOKE 연산 결과의 값 범주를 보존하지 않았음 |
보존함 |
| LWG 3052 ( P2162R2 ) | C++17 | Variants의 어떤 타입이 std::variant가 아니면 효과가 불명확했음 |
명확히 지정됨 |
같이 보기
| visit (C++26) | variant가 보관한 인자로 주어진 함수 객체를 호출해요 (공개 멤버 함수) [edit] |
|---|---|
| swap | 다른 variant와 교환해요 (공개 멤버 함수) [edit] |