std::visit와 std::variant을 이용한 오버로딩
이 내용은 대부분은 https://www.bfilipek.com/2018/09/visit-variants.html 을 참조하였습니다.
std::variant 클래스는 간단히 정의하자면 안전한 union 버전이라고 볼수있다.
union의 최대의 단점은 타입의 값을 집어넣었을때 해당값이 int,string 알수 없다.
이 단점을 회피하기위해 C++ 17에는 std::variant 클래스가 추가되었다.
자세한 설명은 보자.
http://occamsrazr.net/tt/323 https://en.cppreference.com/w/cpp/utility/variant
std::visit함수는 std::variant 클래스에 담겨있는 타입에 맞는 operator을 호출을 해주는 함수이다. 자세한건 아래의 코드를 보자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // ConsoleApplication1.cpp : Defines the entry point for the console application. // #include “stdafx.h” #include <iostream> #include <utility> #include <variant> struct Computer{ }; struct Mp3 {}; struct HandPhone {}; struct hand {}; struct Buy { void operator()(Mp3 &obj) { std::cout << “Mp3” << std::endl; } void operator()(Computer &computer) { std::cout << “Computer” << std::endl; } void operator()(hand &computer) { std::cout << “hand” << std::endl; } void operator()(HandPhone &computer) { std::cout << “HandPhone” << std::endl; } }; int main() { std::variant<Computer, Mp3, HandPhone, hand> weapon{Mp3()}; std::visit(Buy(), weapon); } | cs |
해당 코드를 실행시켜보자. 만약에 VS2017을 사용해도 컴파일이 안되면 http://www.kudryavka.me//?p=268 글을 참조하자.
결과와 알수 있듯이 std::variant의 다양한 타입들중에 현재안에 들어있는 타입에 맞는 오버로딩을 호출하여 std::visit가 호출을 해주는거를 알수있다.
또한 Buy 오버로딩 클래스를 정의하지않고 람다표현식을 이용하여 아래와같이 쓸수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <iostream> #include <utility> #include <variant> template <class …Ts> struct overload : Ts…{using Ts::operator()…; }; template <class …Ts> overload(Ts…)–>overload<Ts…>; struct Computer{ }; struct Mp3 {}; struct HandPhone {}; struct hand {}; int main() { std::variant<Computer, Mp3, HandPhone, hand> weapon{Mp3()}; std::visit(overload{ [](Computer&) {std::cout << “Computer” << std::endl; }, [](Mp3&) {std::cout << “Mp3” << std::endl; }, [](HandPhone&) {std::cout << “HandPhone” << std::endl; }, [](hand&) {std::cout << “hand” << std::endl; } }, weapon); } | cs |
위의 코드를 실행 시키면 아까 전과 동일한 결과가 출력이 된다.
6~7줄의 overload(C++17문법)의 대해 이해가 안가면 다음 링크를 참조하도록 하자.
https://arne-mertz.de/2017/06/class-template-argument-deduction/
또한 std::visit에 더 많은 변수를 담을수 있다. 아래의 함수의 선언을 보게되면 가변 템플릿을 사용한것을 알수있다.
template <class Visitor, class... Variants>
constexpr ReturnType visit(Visitor&& vis, Variants&&... vars);
가변 템플릿의 특성을 이용하여 다음과 같이 작성할수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using namespace std; template <class …Ts> struct overload : Ts…{using Ts::operator()…; }; template <class …Ts> overload(Ts…)–>overload<Ts…>; struct Computer{ }; struct Mp3 {}; struct HandPhone {}; struct hand {}; int main() { std::variant<int, float, char, string> v1{ “asdasda”s }; std::variant<int, float, char, string> v2{ 10 }; std::visit(overload{ [](int a, int b) {}, [](int a, float b) {}, [](int a, char b) {}, [](float a, int b) {}, [](auto a, auto b) {std::cout << “auto” << std::endl; }, //나머지 호출 }, v1, v2); } | cs |
마지막에 auto를 둔 이유는 매칭이 되지않았을경우 해당 operator을 호출하도록 유도한 것이다.
참조 링크 :
https://www.bfilipek.com/2018/09/visit-variants.html
https://gist.github.com/utilForever/824a1bcb3a6b589b1c887e7ede930976