development

값 초기화에 대한 나의 시도는 함수 선언으로 해석되며 왜 A a (());

big-blog 2020. 6. 8. 07:56
반응형

값 초기화에 대한 나의 시도는 함수 선언으로 해석되며 왜 A a (()); 해결?


Stack Overflow가 가르쳐 준 많은 것들 중에는 "가장 vexing parse"라고 알려진 것이 있습니다.

A a(B()); //declares a function

이것은 대부분 직관적 으로 생성자 매개 변수로 임시 객체를 사용하여 a유형 의 객체 선언하는 것처럼 보이지만 실제로는 매개 변수 를 반환 하지 않고 반환하는 함수에 대한 포인터를 사용하여 실제로 는를 반환하는 함수의 선언입니다. . 마찬가지로 선ABaAB

A a(); //declares a function

객체 대신 함수를 선언하기 때문에 같은 범주에 속합니다. 이제 첫 번째 경우이 문제에 대한 일반적인 해결 방법 B()은 컴파일러가 객체 선언으로 해석하므로 괄호 / 괄호를 추가하는 것입니다.

A a((B())); //declares an object

그러나 두 번째 경우 동일한 작업을 수행하면 컴파일 오류가 발생합니다.

A a(()); //compile error

내 질문은 왜? 예, 올바른 '해결 방법'이로 변경하는 것임을 잘 알고 A a;있지만 ()첫 번째 예제에서 컴파일러 의 추가 기능 이 다시 적용 할 때 작동하지 않는 것이 무엇인지 알고 싶습니다 . 두 번째 예. 는 IS A a((B()));해결 방법은 표준에 기록 특정 예외?


깨달은 대답은 없습니다. C ++ 언어로 유효한 구문으로 정의되지 않았기 때문입니다. 따라서 언어의 정의에 따라 결정됩니다.

안에식이 있으면 유효합니다. 예를 들면 다음과 같습니다.

 ((0));//compiles

더 간단한 설명 : (x)유효한 C ++식이 기 때문에 ()그렇지 않습니다.

언어가 정의되는 방식과 컴파일러의 작동 방식에 대해 자세히 알아 보려면 공식 언어 이론 또는보다 구체적으로 문맥 자유 문법 (CFG) 및 유한 상태 기계와 같은 관련 자료에 대해 학습해야 합니다. wikipedia 페이지로는 충분하지 않지만 책에 대한 정보가 필요합니다.


이 문제의 최종 해결 방법은 가능한 경우 C + 11 균일 한 초기화 구문으로 이동하는 것입니다.

A a{};

http://www.stroustrup.com/C++11FAQ.html#uniform-init


C 함수 선언자

우선, C가 ​​있습니다. C에서는 A a()함수 선언입니다. 예를 들어 putchar다음과 같은 선언이 있습니다. 일반적으로 이러한 선언은 헤더 파일에 저장되지만 함수 선언이 어떻게 보이는지 알면 수동으로 작성하는 것을 막을 수있는 것은 없습니다. 인수 이름은 선언에서 선택 사항이므로이 예제에서는 생략했습니다.

int putchar(int);

이를 통해 이와 같은 코드를 작성할 수 있습니다.

int puts(const char *);
int main() {
    puts("Hello, world!");
}

C를 사용하면 함수 호출처럼 보이는 읽기 쉬운 구문으로 함수를 인수로 취하는 함수를 정의 할 수 있습니다 (함수에 대한 포인터를 반환하지 않는 한 읽을 수 있습니다).

#include <stdio.h>

int eighty_four() {
    return 84;
}

int output_result(int callback()) {
    printf("Returned: %d\n", callback());
    return 0;
}

int main() {
    return output_result(eighty_four);
}

앞서 언급했듯이 C는 헤더 파일에서 인수 이름을 생략 할 수 있으므로 헤더 파일에서 output_result이와 같이 보입니다.

int output_result(int());

생성자에서 하나의 인수

Don't you recognize that one? Well, let me remind you.

A a(B());

Yep, it's exactly the same function declaration. A is int, a is output_result, and B is int.

You can easily notice a conflict of C with new features of C++. To be exact, constructors being class name and parenthesis, and alternate declaration syntax with () instead of =. By design, C++ tries to be compatible with C code, and therefore it has to deal with this case - even if practically nobody cares. Therefore, old C features have priority over new C++ features. The grammar of declarations tries to match the name as function, before reverting to the new syntax with () if it fails.

If one of those features wouldn't exist, or had a different syntax (like {} in C++11), this issue would never have happened for syntax with one argument.

Now you may ask why A a((B())) works. Well, let's declare output_result with useless parentheses.

int output_result((int()));

It won't work. The grammar requires the variable to not be in parentheses.

<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token

However, C++ expects standard expression here. In C++, you can write the following code.

int value = int();

And the following code.

int value = ((((int()))));

C++ expects expression inside inside parentheses to be... well... expression, as opposed to the type C expects. Parentheses don't mean anything here. However, by inserting useless parentheses, the C function declaration is not matched, and the new syntax can be matched properly (which simply expects an expression, such as 2 + 2).

More arguments in constructor

Surely one argument is nice, but what about two? It's not that constructors may have just one argument. One of built-in classes which takes two arguments is std::string

std::string hundred_dots(100, '.');

This is all well and fine (technically, it would have most vexing parse if it would be written as std::string wat(int(), char()), but let's be honest - who would write that? But let's assume this code has a vexing problem. You would assume that you have to put everything in parentheses.

std::string hundred_dots((100, '.'));

Not quite so.

<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
                 from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
     basic_string<_CharT, _Traits, _Alloc>::
     ^

I'm not sure why g++ tries to convert char to const char *. Either way, the constructor was called with just one value of type char. There is no overload which has one argument of type char, therefore the compiler is confused. You may ask - why the argument is of type char?

(100, '.')

Yes, , here is a comma operator. The comma operator takes two arguments, and gives the right-side argument. It isn't really useful, but it's something to be known for my explanation.

Instead, to solve the most vexing parse, the following code is needed.

std::string hundred_dots((100), ('.'));

The arguments are in parentheses, not the entire expression. In fact, just one of expressions needs to be in parentheses, as it's enough to break from the C grammar slightly to use the C++ feature. Things brings us to the point of zero arguments.

Zero arguments in constructor

You may have noticed the eighty_four function in my explanation.

int eighty_four();

Yes, this is also affected by the most vexing parse. It's a valid definition, and one you most likely have seen if you created header files (and you should). Adding parentheses doesn't fix it.

int eighty_four(());

Why is that so? Well, () is not an expression. In C++, you have to put an expression between parentheses. You cannot write auto value = () in C++, because () doesn't mean anything (and even if did, like empty tuple (see Python), it would be one argument, not zero). Practically that means you cannot use shorthand syntax without using C++11's {} syntax, as there are no expressions to put in parenthesis, and C grammar for function declarations will always apply.


You could instead

A a(());

use

A a=A();

The innermost parens in your example would be an expression, and in C++ the grammar defines an expression to be an assignment-expression or another expression followed by a comma and another assignment-expression (Appendix A.4 - Grammar summary/Expressions).

The grammar further defines an assignment-expression as one of several other types of expression, none of which can be nothing (or only whitespace).

So the reason you can't have A a(()) is simply because the grammar doesn't allow it. However, I can't answer why the people who created C++ didn't allow this particular use of empty parens as some sort of special-case - I'd guess that they'd rather not put in such a special case if there was a reasonable alternative.

참고URL : https://stackoverflow.com/questions/1424510/my-attempt-at-value-initialization-is-interpreted-as-a-function-declaration-and

반응형