C ++의 get_from_variant 함수
이것은 A recursive_transform Function For Various Type Nested Iterable With std :: variant Implementation in C ++에 대한 후속 질문입니다 . 으로 G. Sliepen의 대답은 작동 만 반복적으로 떠나 변형 언급에 대한 recursive_transform()더 좋은 생각이있을 수 있습니다. 결과적으로 recursive_transform기능 구현은 다음과 같은 형태로 유지됩니다. 또한 앞으로 선언이 제거되었습니다.
template<class T, class _Fn> requires is_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(), func);
return returnObject;
}
template<class T, class _Fn> requires is_iterable<T> && is_element_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(),
[func](const auto& element)
{
return recursive_transform(element, func);
}
);
return returnObject;
}
그러나 나는 여전히 범위와 std::variant같은 복합 구조를 처리하고 싶습니다 std::vector<std::variant<double>>. get_from_variant이러한 작업에 집중하기 위해 새로운 기능 이 떠 오릅니다.
template<typename T_variant, typename T>
static inline auto get_from_variant(T_variant input_variant)
{
T return_val;
std::visit([&](auto&& arg)
{
return_val = static_cast<T>(arg);
return arg;
},
input_variant);
return return_val;
}
이 get_from_variant기능 의 테스트 :
int main()
{
// get_from_variant function test
std::variant<double> testNumber = 1;
std::cout << get_from_variant<decltype(testNumber), double>(testNumber);
// The usage of recursive_transform function and get_from_variant function
std::variant<double> variant_number = 3.14;
std::vector<decltype(variant_number)> testVector1;
testVector1.push_back(variant_number);
testVector1.push_back(variant_number);
testVector1.push_back(variant_number);
std::cout << get_from_variant<std::variant<double>, double>(recursive_transform(testVector1, [](auto x){ return get_from_variant<std::variant<double>, double>(x) + 1; }).at(0)) << std::endl;
return 0;
}
모든 제안을 환영합니다.
어떤 질문에 대한 후속 조치입니까?
C ++에서 std :: variant 구현을 사용하는 다양한 유형의 중첩 된 Iterable에 대한 recursive_transform 함수
지난 질문 이후 코드에서 어떤 변화가 있었습니까?
더 나은 방법
std::variant으로 범위 및와 함께 복합 구조를 처리하기 위해std::vector<std::variant<double>>새로운 함수get_from_variant가 생성되었습니다.새로운 리뷰가 요청되는 이유는 무엇입니까?
제 생각에는 기능의 디자인이 좋은지 잘 모르겠습니다
get_from_variant. 아이디어 나 사용법이 좋은가요? 모든 의견을 환영합니다.
답변
나는 처음부터이 글을 따르지 않았기 때문에 독자들이이 시점까지 예상했던 것보다 더 혼란 스러워요. 코드의 작은 부분에 질문의 초점을 유지하려면 Godbolt 링크처럼 매번 완전한 컴파일 가능한 예제를 제공하는 것이 좋습니다.
사실, 저는 Godbolt 링크를 보는 것을 선호 합니다 ( 당신이 올바르게 한대로 질문에있는 코드를 보는 것 외에도 ). 그것은 당신의 코드를 Godbolt에 직접 붙여 넣는 수고를 덜어주기 때문입니다. :) 다음은 코드 링크입니다 : Godbolt .
std::variant<double> testNumber = 1;
이것은 C ++ 20에서 컴파일되지 않습니다. 사용 했습니까? 그렇다면, 그것은 C ++에 대한 꽤 큰 API 중단입니다 ...하지만 문제는 아닙니다. 어쨌든 변경 1.0하고 다시 컴파일하십시오.
template<typename T_variant, typename T>
static inline auto
패 static inline. 템플릿 정의에 의해 효율적으로 인라인, 그리고 당신은하지 않습니다 싶어 이 템플릿은 정적으로 - 당신이하지 않는 원하는 (그것이 inliner에 의해 멀리 최적화 아니라고 경우) 고유의 사본을 보관하기 위해 각 변환 장치를 강제로.
나는 팬이 아니다 Giraffe_case. 템플릿 매개 변수 이름은 짧고 CamelCase; 여기에 추천 V합니다.
귀하의 std::visit람다는 쓸모없는 return arg;. 사실,이 전체 기능은
template<class V, class T>
auto get_from_variant(V input) {
return std::visit([&](auto&& arg) {
return static_cast<T>(arg);
}, input);
}
껍데기가 제거되면 다음 단계의 pedantry에 집중할 수있는 뇌 세포가 생깁니다. arg참조 ( auto&&) 를 전달 하여 가져 오지만 실제로는 static_cast. static_cast<T>(static_cast<decltype(arg)>(arg))여기에서 사용해야 할 수도 있습니다 arg. rvalue 참조 인 경우 T의 생성자 로 이동 합니까?
하지만 기다려; 우리가 lvalue를 방문하고 있기 때문에 rvalue 참조 arg가 될 수 없습니다input ! 따라서 우리 arg가 방문한 내용 을 수정할 것으로 예상해서는 안됩니다 const auto& arg.. 그러나 수정을 기대하지 않는다면 input, 아마도 그것을 가져 가야 할 것입니다. 예, 잠깐만 요. 왜 우리가 input여기서 사본을 만들고 있습니까? 시작하려면 const 참조로 가져 가십시오!
template<class V, class T>
auto get_from_variant(const V& input) {
return std::visit([](const auto& arg) {
return static_cast<T>(arg);
}, input);
}
[&]캡처가 필요하지 않기 때문에 람다에서 삭제했습니다 .
에 대한 템플릿 매개 변수도 살펴 봐야합니다 get_from_variant. V추론 할 수 있고 T할 수 없습니다. 항상 추론 할 수없는 매개 변수를 먼저 배치하는 것이 좋습니다.
template<class T, class V>
auto get_from_variant(const V& input) {
return std::visit([](const auto& arg) {
return static_cast<T>(arg);
}, input);
}
이제 주요 드라이버는 다음과 같습니다 .
std::variant<double> testNumber = 1.0;
std::cout << get_from_variant<double>(testNumber);
std::vector testVector1 = {
std::variant<double>(3.14),
std::variant<double>(3.14),
std::variant<double>(3.14),
};
std::cout << get_from_variant<double>(
recursive_transform(testVector1, [](const auto& x){
return get_from_variant<double>(x) + 1;
}).at(0)
) << std::endl;
한편 recursive_transform에는 오타 const T input가 있습니다 const T& input. 이러한 오타를 기계적으로 grep 할 수 있습니다 .
다시,
static inline템플릿 에서 제거하십시오 .이름
_Fn은 구현을 위해 예약되어 있습니다. 그냥 사용하십시오F.func람다로 복사 할 필요는 없습니다.[&]작성하는 모든 람다에 대해 기본값으로 사용해야 합니다 (위와 같이 plain으로 벗어날 수없는 경우[]).솔직히, STL 관용구를 따르는 것에 열광 하지 않는 한 ,
Fconst 참조로 콜백 을 전달하고 복사하지 마십시오. C ++에는 상태 저장 및 변경 가능한 콜백을위한transform장소가 있지만 그 장소는 아닙니다.기본 케이스는 필요 이상으로 복잡합니다. 수정하겠습니다.
함께 모아서:
template<class T, class F>
T recursive_transform(const T& input, const F& f) {
return f(input);
}
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
T returnObject = input;
std::transform(input.begin(), input.end(), returnObject.begin(),
[&](const auto& element) {
return recursive_transform(element, f);
}
);
return returnObject;
}
그리고, 정말 사용하여 나에게 보인다 std::transform여기하는 것은 과잉이다 : 그것은 읽 input 두 번 (가) 변환 할 다시 사본을하고 나면. 이렇게 오픈 코딩했다고 가정 해 봅시다.
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
T output = input;
for (auto&& elt : output) {
elt = recursive_transform(elt, f);
}
return output;
}
물론 우리는 할 수 처럼 뭔가를 20 개 범위 C ++를 사용하는 이 :
template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
auto transformed = input | std::views::transform([&](auto&& x) {
return recursive_transform(x, f);
});
return T(transformed.begin(), transformed.end());
}
컴파일 속도가 느리고 더 큰 코드를 생성합니다. 그러나 T::value_type복사 할당이 제거 되었기 때문에 복사 비용이 많이 드는 경우 런타임에 실제로 더 빠를 수 있습니다. T::value_type우리는 제자리에서 직접 구성하는 것입니다.