decltype (auto)의 용도는 무엇입니까?


151

c ++ 14에는 decltype(auto)관용구가 도입되었습니다.

일반적으로 선언 은 주어진 표현식 에서 규칙 을 사용할 수 있도록 합니다autodecltype .

관용구의 "좋은"사용법의 예를 검색하면 다음과 같은 것 ( Scott Meyers에 의한 것 ), 즉 함수의 반환 유형 공제에 대해서만 생각할 수 있습니다 .

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

이 새로운 언어 기능이 유용한 다른 예가 있습니까?


2
이 게시물은 기본적를 사용할 때 컴파일러에 최적화 적은 옵션을 제공하기 때문에이 관용구를 피하려고하는 것이 좋습니다 stackoverflow.com/a/20092875/2485710
user2485710

나는 한 번 decltype(auto)비슷한 것과 비슷한 것을 template<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }사용 return (p.first);했지만 놀랍게도 작동하는 것을 사용해야한다는 것을 깨달았습니다 (그러나 IIRC는 의도적입니다).
dyp

@ user2485710 최적화에 대한 확신이없는 경우 decltype(auto), 선언 된 객체에 무언가를 복사 / 이동하여 예상에 반하는 사고 가 발생할 가능성이 높아집니다 .
underscore_d

답변:


170

제네릭 코드로 반환 형식 전달

일반적으로 제공 한 초기 예제와 같이 제네릭이 아닌 코드의 경우 참조를 반환 유형으로 가져 오도록 수동으로 선택할 수 있습니다.

auto const& Example(int const& i) 
{ 
    return i; 
}

그러나 일반 코드 에서는 참조 또는 값을 처리하는지 여부를 알지 못하면 반환 유형완벽하게 전달할 수 있기를 원합니다 . decltype(auto)당신에게 그 능력을 제공합니다 :

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}

재귀 템플릿에서 반환 유형 공제 지연

에서 이 Q & A 템플릿의 반환 유형으로 지정되었을 때 몇 일 전, 템플릿 인스턴스화하는 동안 무한 재귀가 발생했습니다 decltype(iter(Int<i-1>{}))대신 decltype(auto).

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }

decltype(auto)여기서 템플릿 인스턴스화 먼지가 발생한 후 리턴 유형 공제지연시키는 데 사용됩니다 .

다른 용도

당신은 또한 사용할 수 있습니다 decltype(auto)초안 표준 예, 다른 상황에서 N3936을 또한 상태를

7.1.6.4 자동 사양 [dcl.spec.auto]

1 autodecltype(auto)유형 지정자는 이니셜 라이저에서 추론하거나 후행 리턴 유형으로 명시 적으로 지정하여 나중에 대체 할 자리 표시 자 유형을 지정합니다. auto형 SPECI 좋 어 또한 람다 일반 람다임을 나타 내기 위해 사용된다.

2 자리 표시 자 유형 선언자가 유효한 모든 컨텍스트에서 decl-specifier-seq, type-specifier-seq, conversion-function-id 또는 trailing-return-type에서 함수 선언자와 함께 나타날 수 있습니다 . 함수 선언자에 후행 반환 형식 (8.3.5)이 포함되어 있으면 선언 된 함수 반환 형식을 지정합니다. 함수의 선언 된 리턴 유형에 플레이스 홀더 유형이 포함 된 경우 함수 본문의 리턴 문에서 함수의 리턴 유형이 추론됩니다 (있는 경우).

초안에는 다음과 같은 변수 초기화 예제도 포함되어 있습니다.

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

17
의 differen 동작입니다 (i)iC ++ 14의 새로운 점은?
Danvil

14
@Danvil 은 이미 C ++ 11 decltype(expr)decltype((expr))다르기 때문에 이러한 동작을 일반화합니다.
TemplateRex

13
방금 이것을 배웠고 끔찍한 디자인 결정처럼 느껴집니다 ... 괄호의 구문 의미에 지키는 뉘앙스를 추가하십시오.
Kahler

이 역겨움을 항상 유발하는 예는 한 줄짜리 파일-문자열 구문입니다 (해당 링크에도 언급). 그것의 모든 부분은 뒤로 보인다. 모호함을 전혀 기대하지 않을 수 있으며, 중복 된 괄호를 샘플에서 강제로 제거하십시오. SFINAE에 따라 제거 프로세스를 통해 모호성이 해결 될 것으로 예상하지만 선언 이외의 후보는 사전에 제거됩니다 (SF AE). 그리고 좌절에서 당신은 임의의 Parens가 모호성을 해결한다고 생각하는 것을 컴파일하자마자 나아갈 수 있지만, 그것들을 소개 합니다. 내가 생각하는 CS101 교수들에게 가장 득이되는 것.
John P

@TemplateRex : 참조 된 질문에서 반환 유형 확인 지연에 대해 : 내가 본 한, 특정 시나리오 에서는 auto결과가 어쨌든 값으로 반환되므로 간단한 작업도 수행했을 것입니다 ... 또는 내가 놓쳤습니까 어떤 것?
Aconcagua

36

여기 에서 물건을 인용 :

  • decltype(auto)전달 함수의 리턴 유형 및 유사한 랩퍼추론하는 데 주로 유용합니다 . 여기서 호출하는 표현식을 유형을 정확히 "추적"하려는 유형이 있습니다.

  • 예를 들어, 아래 기능이 주어지면 :


   string  lookup1();
   string& lookup2();

  • C ++ 11에서는 반환 유형의 참조를 유지하는 것을 기억하는 다음 래퍼 함수를 ​​작성할 수 있습니다.

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • C ++ 14에서는 다음을 자동화 할 수 있습니다.

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

  • 그러나 decltype(auto)그 이상으로 널리 사용되는 기능은 아닙니다.

  • 특히, 로컬 변수선언 하는 데 사용할 수 있지만 로컬 변수의 참조는 초기화 식에 의존해서는 안되기 때문에 반 패턴 일 수 있습니다 .

  • 또한 return 문을 작성하는 방법에 민감합니다.

  • 예를 들어, 아래 두 함수는 서로 다른 리턴 유형을 갖습니다.


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }

  • 첫 번째는 로컬 변수에 대한 참조 인 string두 번째는 반환 string&합니다 str.

제안서 에서 보다 의도 된 용도를 볼 수 있습니다.


3
auto반품에 사용하지 않습니까?
BЈовић

@ BЈовић는 일반화 된 반환 유형 공제 (즉, auto반환) 와 함께 작동 할 수 있지만 OP는의 사용을 구체적으로 요구했습니다 decltype(auto).
101010

3
문제는 여전히 관련이 있습니다. 반환 유형은 auto lookup_a_string() { ... } 무엇입니까? 항상 비 참조 유형입니까? 따라서 auto lookup_a_string() ->decltype(auto) { ... }참조가 (경우에 따라) 반환되도록 강제해야합니까?
Aaron McDaid

@AaronMcDaid Deductible auto은 값별 전달 템플릿이라는 용어로 정의되므로 참조가 될 수 없습니다. 제발 - 대기는 auto물론 대한 참조를 포함하여 무엇이든 할 수있다.
curiousguy

4
언급 할 가치가있는 추가 예제는의 요소를 반환하는 것 std::vector입니다. 당신이 있다고 말하십시오 template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }. 그런 다음 S<bool>::operator[]의 전문화 때문에 매달려있는 참조를 반환합니다 std::vector<bool>. 리턴 유형을 변경 decltype(auto)하여이 문제를 피하십시오.
Xoph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.