그 보인다 auto
새로운 언어를 많이 따를 것으로 보인다 C ++ 11에 추가 할 수있는 매우 중요한 기능이다. 파이썬과 같은 언어와 마찬가지로 명시 적 변수 선언을 보지 못했습니다 (파이썬 표준을 사용할 수 있는지 확실하지 않습니다).
auto
변수를 명시 적으로 선언하는 대신 선언 하는 데 단점이 있습니까?
그 보인다 auto
새로운 언어를 많이 따를 것으로 보인다 C ++ 11에 추가 할 수있는 매우 중요한 기능이다. 파이썬과 같은 언어와 마찬가지로 명시 적 변수 선언을 보지 못했습니다 (파이썬 표준을 사용할 수 있는지 확실하지 않습니다).
auto
변수를 명시 적으로 선언하는 대신 선언 하는 데 단점이 있습니까?
답변:
단점에 대해서만 물어 봤으므로 그 중 일부를 강조하고 있습니다. 잘 사용하면 auto
몇 가지 장점도 있습니다. 단점은 남용이 쉽고 코드가 의도하지 않은 방식으로 작동 할 가능성이 높아짐에 따라 발생합니다.
주요 단점은을 사용하여 auto
생성되는 객체의 유형을 반드시 알 필요는 없다는 것입니다. 프로그래머가 컴파일러가 한 유형을 추론하기를 기대할 수도 있지만 컴파일러는 다른 유형을 강력하게 추론합니다.
다음과 같은 선언이 주어졌습니다.
auto result = CallSomeFunction(x,y,z);
어떤 유형인지 반드시 알 필요는 없습니다 result
. 일 수 있습니다 int
. 포인터 일 수 있습니다. 다른 것일 수도 있습니다. 이들 모두는 다른 작업을 지원합니다. 다음과 같은 사소한 변경으로 코드를 극적으로 변경할 수도 있습니다
auto result = CallSomeFunction(a,y,z);
CallSomeFunction()
결과 유형 에 어떤 오버로드가 존재하는지에 따라 완전히 다를 수 있으므로 후속 코드는 의도 한 것과 완전히 다르게 작동 할 수 있습니다. 나중에 코드에서 오류 메시지를 갑자기 트리거 할 수 있습니다 (예 :을 역 참조 int
하려고 시도하고 현재 무언가 변경하려고 시도 const
). 더 불길한 변경은 변경이 컴파일러를 통과하는 곳이지만 후속 코드는 다르고 알 수없는 버그가있는 방식으로 작동합니다.
따라서 일부 변수 유형에 대한 명확한 지식이 없으면 코드가 의도 한대로 작동한다는 주장을 엄격하게 정당화하기가 더 어려워집니다. 즉, 중요도가 높은 (예 : 안전에 중요하거나 미션 크리티컬) 도메인에서 "목적에 적합"하다는 주장을 정당화하려는 노력이 더 많이 필요합니다.
다른 가장 일반적인 단점은 프로그래머가 auto
코드의 기능에 대해 생각하고 올바른 코드를 만들기 위해 노력하는 대신 코드를 강제로 컴파일하는 무딘 도구 로 사용하려는 유혹입니다 .
auto
이라면 대부분의 오리 유형 언어는 의도적으로 그러한 단점을 겪고 있습니다.
CallSomeFunction()
다른 유형은 인수의 순서에 따라 반환, 그의 설계 결함이다 CallSomeFunction()
,하지의 문제 auto
. 사용하기 전에 사용중인 함수의 설명서를 읽지 않으면 문제가 아니라 프로그래머의 결함입니다 auto
. 하지만 난 당신이 악마의 옹호자를 여기고 있다는 것을 이해합니다.
T CallSomeFunction(T, int, int)
디자인 결함일까요? 분명히 "인수의 순서에 따라 다른 유형을 반환합니다."
auto
생성되는 객체의 유형을 반드시 알 필요는 없다는 것입니다." 왜 이것이 auto
하위 표현식 임시 문제가 아닌에 문제 가 있는지 자세히 설명 할 수 있습니까 ? 왜 auto result = foo();
나쁜가 foo().bar()
?
이것은 auto
원칙적으로 정확하게 단점이 아니지만 실제적인 관점에서는 일부에게는 문제가되는 것 같습니다. 기본적으로 일부 사람들은 a) auto
유형의 구세주로 취급 하고 사용할 때 두뇌를 차단하거나 b) auto
항상 가치 유형으로 추론하는 것을 잊어 버립니다 . 이것은 사람들이 다음과 같은 일을하게합니다 :
auto x = my_obj.method_that_returns_reference();
죄송합니다. 일부 개체를 깊게 복사했습니다. 종종 버그 또는 성능 오류입니다. 그런 다음 다른 방법으로도 스윙 할 수 있습니다.
const auto& stuff = *func_that_returns_unique_ptr();
이제 매달려있는 참조를 얻습니다. 이러한 문제는 전혀 발생 auto
하지 않았으므로 이에 대한 합법적 인 주장은 아닙니다. 그러나 auto
처음에 열거 한 이유 때문에이 문제를 더 개인적인 것으로 보았습니다 (개인 경험에서).
나는 주어진 시간에 사람들이 조정하고 노동의 분열을 이해한다고 생각 auto
합니다. 근본적인 유형을 추론하지만, 당신은 여전히 참조와 정의에 대해 생각하고 싶습니다. 그러나 시간이 조금 걸립니다.
std::vector
). 복사 비용이 많이 드는 것은 클래스의 속성이 아니라 개별 객체의 속성입니다. 따라서 method_that_returns_reference
복사 생성자가 있지만 클래스를 복사하는 데 비용이 많이 들고 이동할 수없는 클래스의 개체를 참조 할 수 있습니다.
std::vector
무엇입니까? (그렇습니다, 또는 클래스를 제어하지 못하기 때문에 그것이 중요하지 않습니다.) 복사하는 데 비용이 많이 들고 (복사 가능하기 때문에 리소스를 소유하지 않는 경우) 왜 객체에 COW를 사용하지 않습니까? 데이터 지역은 이미 개체의 크기에 의해 종료되었습니다.
= delete
과부하입니다. 더 일반적으로 당신이 말하는 것은 해결책입니다. nirfriedman.com/2016/01/18/…에 관심이 있으시다면 제가 살펴본 주제 입니다.
다른 답변은 "변수의 유형이 무엇인지 실제로 모른다"와 같은 단점을 언급하고 있습니다. 나는 이것이 코드의 조잡한 명명 규칙과 관련이 있다고 말합니다. 인터페이스 이름이 명확 하면 정확한 유형 을 신경 쓸 필요가 없습니다 . 물론, auto result = callSomeFunction(a, b);
당신에게 많은 말을하지 않습니다. 그러나 정확한 유형을 신경 쓰지 않고 auto valid = isValid(xmlFile, schema);
사용할 수 있다고 충분히 알려줍니다 valid
. 결국, 그냥 if (callSomeFunction(a, b))
을 사용하면 유형도 알 수 없습니다. 다른 하위 표현식 임시 개체와 동일합니다. 그래서 나는 이것이 실제 단점이라고 생각하지 않습니다 auto
.
가장 큰 단점은 때로는 정확한 반환 유형이 작업하려는 것이 아니라는 것입니다. 실제로 실제 반환 유형이 구현 / 최적화 세부 사항으로 "논리적"반환 유형과 다른 경우가 있습니다. 식 템플릿이 대표적인 예입니다. 우리가 이것을 가지고 있다고 가정 해 봅시다.
SomeType operator* (const Matrix &lhs, const Vector &rhs);
논리적으로, 우리는을 기대할 SomeType
것이며 Vector
, 코드에서 그것을 그대로 취급하고 싶습니다. 그러나 최적화를 위해 우리가 사용하는 대수 라이브러리가 표현식 템플릿을 구현할 수 있으며 실제 반환 유형은 다음과 같습니다.
MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);
이제 문제는 즉 MultExpression<Matrix, Vector>
모든 가능성 상점 A의 의지 const Matrix&
와 const Vector&
내부적으로는; Vector
전체 표현식이 끝나기 전에 로 변환 될 것으로 예상합니다 . 이 코드가 있다면 모든 것이 잘됩니다.
extern Matrix a, b, c;
extern Vector v;
void compute()
{
Vector res = a * (b * (c * v));
// do something with res
}
그러나 auto
여기서 사용했다면 문제가 생길 수 있습니다.
void compute()
{
auto res = a * (b * (c * v));
// Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}
auto
결점은 거의 없다고 생각합니다 . 그리고 프록시 등의 다른 예는 다양한 "문자열 빌더"및 DSL에서 발견되는 유사한 객체를 포함한다.
auto
이전에, 특히 Eigen 라이브러리 에 물렸다 . 디버그 빌드에서 문제가 종종 나타나지 않기 때문에 특히 까다 롭습니다.
auto
일반적으로 유형 검사 auto
가 필요하지 않습니다. 좋은 비교가 아닙니다.
단점 중 하나는 때로는로 선언 할 수 없다는 것 const_iterator
입니다 auto
. 당신은에서 가져온 코드의 예에서 보통 (비 const를) 반복자를 얻을 것이다 이 질문 :
map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");
iterator
당신의지도가 아닌이기 때문에 어떤 경우에도 얻을 수 있습니다 const
. const_iterator
변수 를로 변환하려면 평소와 같이 변수 유형을 명시 적으로 지정하거나지도가의 컨텍스트에 속하도록 메소드를 추출하십시오 find
. (나는 후자를 선호한다. SRP.)
auto city_it = static_cast<const auto&>(map).find("New York")
? 또는 C ++ 17을 사용 auto city_if = std::as_const(map).find("New York")
합니다.
코드를 읽기 어렵게하거나 지루하게 만듭니다. 다음과 같은 것을 상상해보십시오.
auto output = doSomethingWithData(variables);
이제 출력 유형을 파악하려면 doSomethingWithData
기능의 서명을 추적해야 합니다.
auto it = vec.begin();
보다 훨씬 쉽게 읽을 수 std::vector<std::wstring>::iterator it = vec.begin();
있습니다.
이 개발자 처럼 나는 싫어 auto
. 또는 오히려 사람들이 잘못 사용하는 방식이 싫습니다 auto
.
나는 타이핑을 줄이는 것이 아니라auto
일반적인 코드를 작성하는 데 도움이 되는 강력한 견해입니다 .
C ++는 개발 시간을 최소화 하지 않고 강력한 코드를 작성할 수 있도록하는 언어입니다 .
이것은 C ++의 많은 기능에서 상당히 분명하지만 불행히도 타이핑하는 사람들이 타이핑을 시작해야한다고 생각하는 오타를 줄이려는 것과 같은 새로운 기능 중 일부는 불행합니다 . auto
이전 auto
에는 사람들이 typedef
s를 사용 했는데, 이는 typedef
라이브러리 디자이너가 반환 유형이 무엇인지 파악하여 라이브러리가 예상대로 작동하도록 도와 주었기 때문에 훌륭 했습니다. 당신이 사용하는 경우 auto
, 당신은 제어 것을 빼앗아 클래스의 디자이너에서 대신이 요청 컴파일러가 타입이 도구 상자에서 가장 강력한 C ++ 도구 중 하나를 제거하고 위험이있는,해야 알아낼 깨고 자신의 코드를.
당신이 사용하는 경우 일반적으로 auto
때문일 수 있어야 코드가 작동 어떤 합리적 유형 , 하지 당신이 함께 작동해야 유형을 적어 너무 게으른 때문이다. auto
게으름을 돕기 위해 도구로 사용 하는 경우 결국에는 프로그램에서 미묘한 버그 를 도입하기 시작 합니다. 일반적으로을 사용했기 때문에 발생하지 않은 암시 적 변환으로 인해 발생합니다 auto
.
불행히도, 이러한 버그는 짧은 예제에서 설명하기가 어렵습니다. 간결성으로 인해 사용자 프로젝트에서 나오는 실제 예제보다 덜 설득력이 있기는하지만 특정 암시 적 변환 이 필요할 것으로 예상되는 템플릿이 많은 코드에서는 쉽게 발생 합니다. 장소.
예를 원한다면 여기 에 하나가 있습니다 . 그러나 약간의주의 사항 : 코드를 뛰어 넘기고 비판하기 전에 : 많은 유명하고 성숙한 라이브러리가 그러한 암묵적 변환을 중심으로 개발되었으며 불가능하지는 않지만 어려울 수있는 문제 를 해결하기 때문에 거기 에 있습니다. 그렇지 않으면 해결하기 위해. 비판하기 전에 더 나은 해결책 을 찾아보십시오 .
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be
실제로 좋은 이유는 아닙니다. 예를 들어 Visual Studio 2015와 같은 최신 IDE를 사용하면 위로 마우스를 가져 가면 변수 유형을 확인할 수 있습니다 auto
. 이것은 정확히 * 와 동일합니다 typedef
.
typename std::iterator_traits<It>::value_type
. (2) 전체 포인트는 추정 된 유형의 필요가 있다고했다 하지 올바른 유형 코드의 이전 설계자가 의도 한대로 "동일"이; 를 사용 auto
하면 올바른 유형을 지정할 수있는 디자이너의 능력이 없어집니다.
vector<bool>
넌센스" ... 사면? 어떻게 bitset
구현되고 있다고 생각 하십니까? 아니면 비트 컨테이너가 모두 넌센스라고 생각하십니까?!
auto
그 자체로 는 단점이 없으며 새로운 코드의 모든 곳에서 (손으로 물결 치기) 옹호합니다. 코드가 일관되게 형식을 검사하고 자동 슬라이싱을 피할 수 있습니다. (경우 B
에서 유래 A
와 함수가 반환 A
갑자기 반환 B
하고 auto
반환 값을 저장하는 데 예상대로 동작)
그러나 C ++ 11 이전의 레거시 코드는 명시 적으로 형식화 된 변수를 사용하여 유도 된 암시 적 변환에 의존 할 수 있습니다. 명시 적으로 유형이 지정된 변수를 auto
변경하면 코드 동작이 변경 될 수 있으므로주의를 기울이는 것이 좋습니다.
auto
그 자체로 단점이 있습니다 (또는 적어도 많은 사람들이 그렇게 생각합니다). 이 패널의 Sutter, Alexandrescu 및 Meyers와의 토론 에서 두 번째 질문에 제공된 예를 고려하십시오. 가지고 auto x = foo(); if (x) { bar(); } else { baz(); }
있고 foo()
반환 bool
하는 경우 foo()
열거 형을 반환하도록 변경하면 (두 가지 대신 세 가지 옵션) 어떻게됩니까? auto
코드는 계속 작동하지만, 예기치 않은 결과를 생성합니다.
bool
대신 auto
변경 하여 사용 합니까? 나는 틀릴 수 있지만 (여기에서 확인할 수는 없다) 유일한 차이점은 bool
의 조건을 평가하는 대신 변수 선언에서 변환이 발생한다는 것입니다 if
. enum
이 범위에 해당하는 경우, bool
명시적인 통지 없이는 로 변환 할 수 없습니다.
키워드는 auto
단순히 반환 값에서 유형을 추론합니다. 따라서 파이썬 객체와 동일하지 않습니다.
# Python
a
a = 10 # OK
a = "10" # OK
a = ClassA() # OK
// C++
auto a; // Unable to deduce variable a
auto a = 10; // OK
a = "10"; // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0; // OK, implicit casting warning
auto
컴파일 중에 추론 되기 때문에 런타임에 어떤 단점도 없습니다.
type()
파이썬에서 수행하는 작업 을 수행합니다. 유형을 추론하고 해당 유형의 새 변수를 만들지 않습니다.
decltype
. auto
변수 할당을위한 것입니다.
지금까지 아무도 언급하지 않았지만, 당신이 저에게 물었다면 그 자체로 가치가 있습니다.
때문에 (모두가 알고 있어야하는 경우에도 C != C++
C로 작성된 코드가 쉽게 C ++ 호환 너무 많은 노력없이 설계 할 수 있으므로 C ++ 코드를 기반을 제공하도록 설계 될 수있다),이 디자인에 대한 요구 사항이 될 수 있습니다.
잘 정의 된 구문 중 일부 C
가 유효하지 않은 규칙에 대해 알고 C++
있습니다. 그러나 이것은 단순히 실행 파일이 손상되고 알려진 UB-clause가 적용되어 대부분의 시간이 이상한 루핑으로 인해 충돌이나 그 밖의 결과가 발생합니다 (또는 감지되지 않을 수도 있지만 여기서는 중요하지 않습니다).
그러나 이것이 auto
처음으로 바뀌는 것은 1입니다 !
auto
스토리지 클래스 지정자로 사용하여 코드를 전송 했다고 가정하십시오 . 그것은 심지어 그것이 사용 된 방식에 따라 반드시 "휴식"일 필요는 없다. 실제로는 프로그램의 동작을 자동으로 변경할 수 있습니다.
그것은 명심해야 할 것입니다.
1 최소한 내가 처음 알았을 때.
int
C 에서 "no type impimps "에 의존하고 있다면 이것으로부터 얻을 수있는 모든 나쁜 것들이 필요하다. 그리고 의존하지 않는다면 , 타입과 함께auto
스토리지 클래스 지정자로 사용 하면 C ++ (이 경우 좋은 것)에서 멋진 컴파일 오류가 발생합니다.
이 답변 에서 설명했듯이 auto
때로는 의도하지 않은 펑키 한 상황이 발생할 수 있습니다. 포인터 유형을 만들 수있는 auto&
동안 참조 유형이 있다고 명시 적으로 말해야 auto
합니다. 이로 인해 지정자를 모두 생략하면 혼동이 생겨 실제 참조 대신 참조 사본이 생성됩니다.
auto
참조와 const
유형을 결코 유추하지 않는 것 입니다. 를 들어 auto
참조, 당신은 더 나은 사용을 거라고 auto&&
. (범용 참조) 유형이 자원을 복사하기에 저렴하지 않거나 소유하지 않는 경우, 유형을 처음부터 복사 할 수 없어야합니다.
다른 자극적 인 예 :
for (auto i = 0; i < s.size(); ++i)
부호있는 int comparison between signed and unsigned integer expressions [-Wsign-compare]
이므로 경고 ( )를 생성합니다 i
. 이것을 피하려면 다음과 같이 작성해야합니다.
for (auto i = 0U; i < s.size(); ++i)
또는 아마도 더 낫습니다 :
for (auto i = 0ULL; i < s.size(); ++i)
size
리턴을 가정 할 때 와 같은 리터럴 size_t
을 가질 수 있어야합니다 . 그러나이를 수행하기 위해 UDL을 선언 할 수 있습니다. ( )size_t
0z
size_t operator""_z(...)
unsigned
의 모든 가치를 수용 할만큼 충분히 크지 않을 가능성이 높기 std::size_t
때문에, 누군가가 엄청나게 거대한 수의 요소를 가진 컨테이너를 가지고 unsigned
있을 경우 , 사용 하면 하위 범위에 무한 루프가 발생할 수 있습니다 지수. 이것이 문제가되지는 않지만 std::size_t
의도를 올바르게 나타내는 깨끗한 코드를 얻으려면 사용해야합니다. unsigned long long
실제로는 반드시 동일해야하지만 확실 하게 확실하다고 확신하지는 않습니다 .
unsigned long long
최소 64 비트가 보장되지만 이론적으로 size_t
는 이것보다 클 수 있습니다. 물론 컨테이너에 2 ^ 64 개 이상의 요소가 있다면 걱정할 큰 문제가있을 수 있습니다 ... ;-)
내가 생각하는 auto
독자가 쉽게 & 분명히 유형을 공제 할 수있는, 지역화 된 문맥에서 사용하거나 아니라 해당 유형의 코멘트 나 이름으로 문서화 할 때 좋은 실제의 형태 추론이. 그것이 어떻게 작동하는지 이해하지 못하는 사람들은 그것을 대신 사용하는 것과 같은 잘못된 방법으로 그것을 이용할 수 있습니다 template
. 내 의견으로는 좋고 나쁜 사용 사례가 있습니다.
void test (const int & a)
{
// b is not const
// b is not a reference
auto b = a;
// b type is decided by the compiler based on value of a
// a is int
}
좋은 용도
반복자
std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();
..
std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();
// VS
auto vi = v.begin();
함수 포인터
int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
..
}
..
int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;
// VS
auto *f = test;
나쁜 용도
데이터 흐름
auto input = "";
..
auto output = test(input);
기능 서명
auto test (auto a, auto b, auto c)
{
..
}
사소한 사례
for(auto i = 0; i < 100; i++)
{
..
}
int
, 당신은 갈 경우 하나 이상의 문자를 입력해야합니다 auto
. 받아 들일 수없는
int
쉽게 볼 수 있고 타이핑 int
이 짧습니다. 그렇기 때문에 사소한 경우입니다.
아무도 이것을 언급하지 않은 것에 놀랐지 만, 당신이 무언가의 계승을 계산한다고 가정 해보십시오.
#include <iostream>
using namespace std;
int main() {
auto n = 40;
auto factorial = 1;
for(int i = 1; i <=n; ++i)
{
factorial *= i;
}
cout << "Factorial of " << n << " = " << factorial <<endl;
cout << "Size of factorial: " << sizeof(factorial) << endl;
return 0;
}
이 코드는 다음을 출력합니다 :
Factorial of 40 = 0
Size of factorial: 4
그것은 분명히 예상 된 결과가 아니 었습니다. 때문에 그 일이 auto
같은 변수 요인의 유형을 추론 int
이 할당 되었기 때문에 1
.