GCC 4.8.0이 출시되면 C ++ 14의 일부인 자동 반환 유형 공제를 지원하는 컴파일러가 있습니다. 를 사용하면 다음 -std=c++1y
과 같이 할 수 있습니다.
auto foo() { //deduced to be int
return 5;
}
내 질문은 :이 기능을 언제 사용해야합니까? 언제 그리고 언제 코드를 더 깨끗하게해야합니까?
시나리오 1
내가 생각할 수있는 첫 번째 시나리오는 가능할 때마다입니다. 이런 식으로 작성할 수있는 모든 기능이 있어야합니다. 이것의 문제점은 코드를 항상 더 읽기 쉽게 만들 수는 없다는 것입니다.
시나리오 2
다음 시나리오는 더 복잡한 반환 유형을 피하는 것입니다. 매우 가벼운 예로서 :
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
나는 그것이 실제로 문제가 될 것이라고는 생각하지 않지만, 반환 유형이 매개 변수에 명시 적으로 의존하는 것이 어떤 경우에는 더 명확 할 수 있다고 생각합니다.
시나리오 3
다음으로 중복을 방지하려면
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
C ++ 11에서는 때로는 return {5, 6, 7};
벡터 대신에 항상 작동 할 수는 있지만 항상 작동하지는 않으며 함수 헤더와 함수 본문 모두에서 유형을 지정해야합니다. 이는 순전히 중복이며, 자동 반환 유형 공제는 이러한 중복을 방지합니다.
시나리오 4
마지막으로 매우 간단한 함수 대신 사용할 수 있습니다.
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
그러나 때로는 정확한 유형을 알고 싶을 때 함수를 볼 수 있으며, 제공되지 않으면 코드의 다른 지점으로 이동해야합니다 pos_
.
결론
이러한 시나리오를 제시하면 실제로이 기능이 코드를 더 깨끗하게 만드는 데 유용한 상황은 무엇입니까? 여기서 언급하지 않은 시나리오는 어떻습니까? 나중에 물지 않도록이 기능을 사용하기 전에 어떤 예방 조치를 취해야합니까? 이 기능이 없으면 불가능한 새로운 기능이 있습니까?
여러 질문은 이에 대한 답을 찾는 관점을 찾는 데 도움을주기위한 것입니다.
답변
C ++ 11은 람다에서 반환 유형 공제를 사용할 때와 auto
변수 를 사용할 때와 비슷한 질문을 제기 합니다.
C와 C ++ 03의 질문에 대한 기존의 대답은 “표현 경계를 넘어서 명시 적으로 표현하는 것입니다. 표현식 내에서 일반적으로 암시 적이지만 캐스트로 명시 적으로 만들 수 있습니다”. C ++ 11 및 C ++ 1y는 유형 공제 도구를 도입하여 새로운 장소에서 유형을 생략 할 수 있습니다.
죄송 합니다만 일반적인 규칙을 적용하여이 문제를 미리 해결할 수는 없습니다. 특정 코드를 살펴보고 어디에서나 유형을 지정하는 가독성에 도움이되는지 여부를 스스로 결정해야합니다. 코드에서 “이 유형은 X”라고 말하는 것이 더 낫거나 더 낫습니다. “이것의 타입은 코드의이 부분을 이해하는 것과 무관하다 : 컴파일러는 알아야하고 우리는 아마 그것을 해결할 수는 있지만 여기서 말할 필요는 없다”고 말하는가?
“가독성”은 객관적으로 정의되지 않았으며 [*] 독자에 따라 다양하기 때문에 스타일 가이드가 전적으로 만족할 수없는 코드의 작성자 / 편집자로서 책임을 져야합니다. 스타일 가이드가 규범을 규정하는 정도까지도, 다른 사람들은 다른 규범을 선호하고 “읽기 어려운”것으로 생소한 것을 발견하는 경향이 있습니다. 따라서 특정 제안 된 스타일 규칙의 가독성은 종종 다른 스타일 규칙의 맥락에서만 판단 할 수 있습니다.
모든 시나리오 (첫 번째조차도)는 누군가의 코딩 스타일에 사용됩니다. 개인적으로 나는 두 번째가 가장 강력한 유스 케이스라고 생각하지만 문서 도구에 따라 달라질 것으로 예상합니다. 함수 템플릿의 반환 유형이로 auto
문서화되어 있지만 문서화 된 것으로보고하면 decltype(t+u)
(희망적으로) 신뢰할 수있는 게시 된 인터페이스를 만드는 것으로 문서화 된 것을 보는 것이 별로 도움이되지 않습니다 .
[*] 때때로 누군가가 객관적인 측정을 시도합니다. 누구나 통계적으로 유의하고 일반적으로 적용 가능한 결과를 내놓을 정도의 적은 정도로, “읽을 수있는”것에 대한 저자의 본능에 찬성하여 일하는 프로그래머에 의해 완전히 무시됩니다.
답변
일반적으로 함수 반환 유형은 함수를 문서화하는 데 큰 도움이됩니다. 사용자는 예상되는 것을 알 수 있습니다. 그러나 중복을 피하기 위해 반환 유형을 삭제하는 것이 좋을 것이라고 생각되는 경우가 있습니다. 예를 들면 다음과 같습니다.
template<typename F, typename Tuple, int... I>
auto
apply_(F&& f, Tuple&& args, int_seq<I...>) ->
decltype(std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...))
{
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template<typename F, typename Tuple,
typename Indices = make_int_seq<std::tuple_size<Tuple>::value>>
auto
apply(F&& f, Tuple&& args) ->
decltype(apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices()))
{
return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}
이 예는 공식위원회 논문 N3493에서 발췌 한 것 입니다. 함수의 목적은 apply
a의 요소를 함수 로 전달 std::tuple
하고 결과를 반환하는 것입니다. int_seq
및 make_int_seq
구현의 일부분이며, 아마 단지 그것이 무엇을 이해하려고 노력하는 모든 사용자를 혼란스럽게합니다.
보다시피, 리턴 타입은 decltype
리턴 된 표현식에 지나지 않습니다 . 또한 apply_
사용자가 볼 수있는 것이 아니기 때문에 반환 유형이 apply
‘ 와 같거나 적을 때 문서화하는 것이 유용하다는 것을 확신하지 못합니다 . 이 특별한 경우 반환 유형을 삭제하면 함수를 더 읽기 쉽게 만듭니다. 이 반환 유형은 표준 N3915decltype(auto)
에 추가 apply
하기 위해 제안서에서 실제로 삭제되고 대체되었습니다 (또한 원래의 대답은이 문서보다 우선 합니다).
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return forward<F>(f)(get<I>(forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices = make_index_sequence<tuple_size<decay_t<Tuple>>::value>;
return apply_impl(forward<F>(f), forward<Tuple>(t), Indices{});
}
그러나 대부분의 경우 반환 유형을 유지하는 것이 좋습니다. 위에서 설명한 특정 경우에 반환 유형은 읽을 수 없으며 잠재적 인 사용자는이를 알 수 없습니다. 예제가 포함 된 좋은 문서가 훨씬 유용 할 것입니다.
상태 : 아직 언급되지 않은 또 다른 것은 declype(t+u)
사용할 수 있도록 표현 SFINAE을 , decltype(auto)
(비록하지 않는 제안이 이 동작을 변경할 수). 예를 들어, foobar
유형의 foo
멤버 함수가 존재하는 경우이를 호출하거나 유형의 멤버 함수가 존재하는 경우이를 호출 하는 함수 를 예로 들어 bar
클래스가 항상 정확 foo
하거나 bar
한 번에 둘 다를 갖지 않는다고 가정하십시오 .
struct X
{
void foo() const { std::cout << "foo\n"; }
};
struct Y
{
void bar() const { std::cout << "bar\n"; }
};
template<typename C>
auto foobar(const C& c) -> decltype(c.foo())
{
return c.foo();
}
template<typename C>
auto foobar(const C& c) -> decltype(c.bar())
{
return c.bar();
}
호출 foobar
의 인스턴스 것은 X
표시됩니다 foo
호출하는 동안 foobar
의 인스턴스에 Y
의지 표시 bar
. 대신 자동 반환 유형 공제를 사용하면 (또는 포함하지 않음 decltype(auto)
) SFINAE 표현식을 얻지 못 하거나 foobar
인스턴스를 호출 X
하거나 Y
컴파일 타임 오류가 발생합니다.
답변
절대 필요하지 않습니다. 당신이해야 할 시점에 관해서는 그것에 대해 많은 다른 대답을 얻을 것입니다. 나는 실제로 표준의 일부로 받아 들여지고 대부분의 주요 컴파일러가 같은 방식으로 잘 지원할 때까지 전혀 말하지 않을 것입니다.
그 외에도 종교적인 논쟁이 될 것입니다. 개인적으로 실제 반환 유형을 입력하지 않으면 코드가 더 명확 해지고 유지 보수가 훨씬 쉬워집니다 (함수의 서명을보고 그것이 무엇을 리턴하는지 실제로 코드를 읽어야 함을 알 수 있음). 한 유형을 반환해야한다고 생각하고 컴파일러는 다른 모든 원인을 생각합니다 (내가 사용한 모든 스크립팅 언어에서 발생한 것처럼). 나는 자동차가 큰 실수라고 생각하며, 그것은 도움보다 훨씬 더 많은 고통을 초래할 것입니다. 다른 사람들은 프로그래밍 철학에 따라 항상 사용해야한다고 말합니다. 여하튼, 이것은이 사이트의 범위를 벗어납니다.
답변
함수의 단순 성과는 아무런 관련이 없습니다 ( 이 질문의 삭제 된 복제본 으로 가정).
반환 유형은 고정되어 auto
있거나 ( 사용하지 않음 ) 템플릿 매개 변수에 따라 복잡한 방식으로 의존합니다 ( auto
대부분의 경우 decltype
여러 반환 지점이있을 때 사용).
답변
실제 생산 환경을 고려하십시오 : 많은 함수와 단위 테스트는 모두의 반환 유형에 상호 의존적입니다 foo()
. 이제 리턴 유형이 어떤 이유로 든 변경되어야한다고 가정하십시오.
반환 유형이 auto
어디에나 있고 호출자 foo()
및 관련 함수 auto
가 반환 된 값을 가져올 때 사용하는 경우 변경해야 할 사항이 최소화됩니다. 그렇지 않다면 이는 매우 지루하고 오류가 발생하기 쉬운 시간을 의미 할 수 있습니다.
실제 예를 들어, 원시 포인터 사용에서 스마트 포인터로 모듈을 변경하라는 요청을 받았습니다. 단위 테스트 수정은 실제 코드보다 더 힘들었습니다.
이것을 처리 할 수있는 다른 방법이 있지만 auto
반환 유형을 사용 하는 것이 좋습니다.
답변
반환 유형 자동이 완벽한 예를 제공하고 싶습니다.
긴 후속 함수 호출을 위해 짧은 별명을 작성한다고 가정하십시오. auto를 사용하면 원래 반환 유형을 처리 할 필요가 없으며 (향후 변경 될 수 있음) 사용자는 원래 기능을 클릭하여 실제 반환 유형을 얻을 수 있습니다.
inline auto CreateEntity() { return GetContext()->GetEntityManager()->CreateEntity(); }
추신 : 이 질문에 달려 있습니다 .
답변
시나리오 3의 경우 반환되는 로컬 변수를 사용하여 반환 유형의 함수 서명을 설정합니다. 클라이언트 프로그래머가 함수가 반환하는 것을 더 명확하게합니다. 이처럼 :
시나리오 3
중복을 방지하려면
std::vector<std::map<std::pair<int, double>, int>> foo() {
decltype(foo()) ret;
return ret;
}
예, 자동 키워드는 없지만 중복을 방지하고 소스에 액세스 할 수없는 프로그래머에게 더 쉬운 시간을 제공하기 위해 교장은 동일합니다.