왜 gcc에서 두 개의 using 절이 동일한 유형으로 해석되어 모호한 것으로 보입니다.


32

using 절을 사용하는 두 가지 기본 클래스가 있습니다.

 class MultiCmdQueueCallback {
  using NetworkPacket  = Networking::NetworkPacket;
  ....
 }


 class PlcMsgFactoryImplCallback {
   using NetworkPacket = Networking::NetworkPacket;
  ....
 }

그런 다음 수업을 선언합니다

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {
  private:
    void sendNetworkPacket(const NetworkPacket &pdu);
}

그런 다음 컴파일러는 'NetworkPacket'에 대한 오류 참조를 모호합니다. 'sendNetworkPacket (NetworkPacket & ...'

이제 두 '사용 절'은 동일한 기본 클래스 인 Networking : NetworkPacket으로 해석됩니다.

실제로 메소드 선언을 다음과 같이 바꾸면

 void sendNetworkPacket(const Networking::NetworkPacket &pdu);

잘 컴파일됩니다.

컴파일러가 각각의 using 절이 동일한 기본 유형을 가리켜도 고유 한 유형으로 취급하는 이유는 무엇입니까? 이것이 표준에 의해 지시됩니까, 아니면 컴파일러 버그가 있습니까?


컴파일러가 충분히 영리하지 않은 것 같습니다
idris

이 시점에서 컴파일러라는 점은 NetworkPacketMultiCmdQueueCallback, PlcMsgFactoryImplCallback, 네트워킹에 세 가지가 있음을 알고 있습니다. 사용할 것을 지정하십시오. 퍼팅 virtual이 여기서 도움이 될 것이라고 생각하지 않습니다 .
theWiseBro

@idris : 대신 표준이 충분하지 않다는 것을 의미했습니다. 컴파일러는 표준을 준수 할 권리가 있습니다.
Jarod42

@ Jarod42 아래의 대답은 'type-id로 표시된 유형의 동의어'이므로 동일한 유형 ID를 가진 경우 두 가지를 모두 사용할 수 있습니다. standart이든 컴파일러이든, 누군가 실제로 실제로 영리하지 않은 것 같습니다.
idris

다중 상속 문제 중 하나
eagle275

답변:


28

별명 결과 유형 및 액세스 가능성을보기 전에

우리는 이름 을 본다

실제로

NetworkPacket 아마도

  • MultiCmdQueueCallback::NetworkPacket
  • 또는 PlcMsgFactoryImplCallback::NetworkPacket

둘 다 지적하는 사실 Networking::NetworkPacket은 관련이 없습니다.

이름을 확인하면 모호성이 생깁니다.


실제로 이것은 부분적으로 만 적용됩니다 PlcNetwork에 용도를 추가하면 NetworkPacket 사용 = MultiCmdQueueCallback :: NetworkPacket; 이전 using 절이 private이므로 컴파일러 오류가 발생합니다.
Andrew Goedhart

@AndrewGoedhart 모순이 아닙니다. 이름 조회는 먼저 자체 클래스에서 시작합니다. 그런 다음 컴파일러가 이미 고유 한 이름을 찾으면 만족합니다.
아콩 카과

내 문제는 여기서 기본 클래스의 개인 명명 절에서 이름이 전파되는 이유입니다. 개인 선언 중 하나를 제거하는 경우, 즉 기본 클래스 중 하나에 개인 using 절이 있고 다른 하나는 private using 절을 갖지 않으면 오류가 'Network Packet은 이름을 지정하지 않습니다'로 변경됩니다.
Andrew Goedhart

1
@AndrewGoedhart Name lookup은 (명백히) 접근성을 고려하지 않습니다. 하나를 공개하고 다른 하나를 비공개로 설정하면 동일한 오류가 발생합니다. 이것이 발견 된 첫 번째 오류이므로 인쇄 할 첫 번째 오류입니다. 하나의 별명을 제거하면 모호성 문제는 사라지지만 불충분 한 문제는 남아 있으므로 다음 오류가 인쇄됩니다. 그건 그렇고, 좋은 오류 메시지 (MSVC 한 번 더?)가 아니라 GCC가 더 정확합니다 : error: [...] is private within this context.
아콩 카과

1
@AndrewGoedhart 다음 사항을 고려하십시오. class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }– 동일하지는 않지만 과부하 해결 방법은 동일하게 작동합니다. 적절한 기능을 선택한 후에 만 ​​사용 가능한 모든 기능을 고려하십시오. 두 개의 문자를 허용하도록 privat 함수를 변경하면 개인용이지만 선택되어 다음 컴파일 오류가 발생합니다.
아콩 카과

14

사용하려는 것을 수동으로 선택하여 모호성을 해결할 수 있습니다.

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {

using NetworkPacket= PlcMsgFactoryImplCallback::NetworkPacket; // <<< add this line
private:
    void sendNetworkPacket(const NetworkPacket &pdu);

}

컴파일러는 기본 클래스의 정의 만 찾습니다. 두 기본 클래스에 동일한 유형 및 / 또는 별칭이 있으면 사용할 클래스를 알지 못한다고 불평합니다. 결과 유형이 동일한 지 여부는 중요하지 않습니다.

컴파일러는 첫 번째 단계에서만 이름을 찾습니다.이 이름이 함수, 유형, 별명, 메소드 또는 기타의 경우 완전히 독립적입니다. 이름이 모호한 경우 컴파일러에서 추가 조치가 수행되지 않습니다! 단순히 오류 메시지를 표시하고 중지합니다. 따라서 주어진 using 문으로 모호성을 해결하십시오.


문구에 대해 의심을 가지십시오. 가 조회하면 정의 , 그것은뿐만 아니라 다음 유형을 고려하지 않을까요? 이름 만 찾아 보지 않습니까 (정의 된 방법을 잊어 버리십시오)? 표준에 대한 일부 참조는 훌륭 할 것입니다 ...
Aconcagua

이 마지막 주석은 이유를 올바르게 설명 합니다. 마지막 문단을이 주석으로 바꾸면 투표 할 것이다.;)
Aconcagua

나는 용납 할 수 없다 – 나는 문제의 저자가 아니다 ... 내가 당신의 신경에 들어간 것이라면 미안하다. 이전에 QA의 핵심 질문에 대답하지 않았다고 느꼈기 때문에 답변을 개선하려고 노력했습니다.
Aconcagua

@Aconcagua : Ubs, 내 잘못 :-) 개선 주셔서 감사합니다!
클라우스

실제로 두 using 절이 모두 비공개이기 때문에 작동하지 않습니다. PlcNetwork에 용도를 추가 한 경우 : | NetworkPacket 사용 = MultiCmdQueueCallback :: NetworkPacket; 이전 using 절이 private이므로 컴파일러 오류가 발생합니다. 그건 그렇고 공용 클래스와 개인 클래스를 사용하여 하나의 기본 클래스를 만들면 여전히 모호성 오류가 발생합니다. 기본 클래스에 정의되지 않은 메소드에 대해 모호한 오류가 발생합니다.
Andrew Goedhart

8

로부터 문서 :

타입 별명 선언은 type-id로 표시된 타입의 동의어로 사용될 수있는 이름을 소개합니다. 새 유형을 도입하지 않으며 기존 유형 이름의 의미를 변경할 수 없습니다.

이 두 using절은 같은 유형을 나타내지 만 컴파일러는 다음과 같은 상황에서 두 가지를 선택할 수 있습니다.

void sendNetworkPacket(const NetworkPacket &pdu);

다음 중에서 선택할 수 있습니다.

  • MultiCmdQueueCallback::NetworkPacket
  • PlcMsgFactoryImplCallback::NetworkPacket

기본 클래스 MultiCmdQueueCallbackPlcMsgFactoryImplCallback기본 클래스 모두에서 상속되기 때문 입니다. 컴파일러 이름 확인의 결과로 모호한 오류가 발생합니다. 이 문제를 해결하려면 컴파일러에서 다음과 같이 하나 이상을 사용하도록 명시 적으로 지시해야합니다.

void sendNetworkPacket(const MultiCmdQueueCallback::NetworkPacket &pdu);

또는

void sendNetworkPacket(const PlcMsgFactoryImplCallback::NetworkPacket &pdu);

솔직히 말해서, 나는 만족스럽지 않습니다 ... 그들은 같은 유형의 동의어입니다. 나는 쉽게 class C { void f(uint32_t); }; void C::f(unsigned int) { }(별명 일치를 제공) 가질 수 있습니다 . 왜 차이점이 있습니까? 그들은 여전히 ​​같은 유형입니다, 당신의 인용에 의해 확인 (나는 설명하기에 충분하지 않습니다) ...
Aconcagua

@Aconcagua : 기본 유형 또는 별명을 사용하면 차이가 없습니다. 별명은 결코 새로운 유형이 아닙니다. 관찰은 두 가지 기본 클래스에서 SAME 별명을 제공하여 생성하는 모호성과 아무 관련이 없습니다.
클라우스

1
@Aconcagua 나는 당신이 언급 한 예가 문제의 상황에 맞지 않는다고 생각합니다
NutCracker

글쎄, 조금 변경하자 : 클래스 A, B 및 C와 typedef D의 이름을 지정하면 다음과 같이 할 수 있습니다. class C : public A, public B { void f(A::D); }; void C::f(B::D) { }– 최소한 GCC가 허용합니다.
아콩 카과

질문 작성자는 문자 그대로 '왜 컴파일러는 각각의 using 절이 동일한 기본 유형을 가리켜도 고유 한 유형으로 취급합니까?' - 내가 인용이를 명확히 할 방법이 표시되지 않는 이유는 음주 대답은 말하고 싶지 ... 대신, 그냥 품질 보증의 혼란을 확인 잘못 ,하지만 내 눈을 충분히 명확하지 않습니다 .. .
아콩 카과

2

두 가지 오류가 있습니다.

  1. 개인 유형 별명에 액세스
  2. 타입 별칭에 대한 모호한 참조

개인-개인

순서가 중요하지 않기 때문에 컴파일러가 두 번째 문제에 대해 먼저 불평하는 문제는 보이지 않습니다. 두 가지 문제를 해결하여 계속 진행해야합니다.

대중

당신은 모두의 가시성을 변경하는 경우 MultiCmdQueueCallback::NetworkPacketPlcMsgFactoryImplCallback::NetworkPacket중 공용 또는 보호, 다음 두 번째 문제 (모호성)에이 분명하다 - 그들은 동일한 기본 데이터 형식을 가지고 있지만, 그 두 개의 서로 다른 유형의 별칭입니다. 어떤 사람들은 "영리한"컴파일러가 당신을 위해 이것을 해결할 수 있다고 생각할 수도 있지만, 컴파일러는 "일반적으로 생각하고"대 / 소문자를 제외한 예외에 대한 전역 규칙을 기반으로 의사 결정을해야한다는 것을 명심하십시오. 다음과 같은 경우를 상상해보십시오.

class MultiCmdQueueCallback {
    using NetworkPacketID  = size_t;
    // ...
};


class PlcMsgFactoryImplCallback {
    using NetworkPacketID = uint64_t;
    // ...
};

컴파일러가 둘 다 NetworkPacketID동일하게 취급해야합니까 ? 확실하지 않습니다. 32 비트 시스템에서는 size_t길이가 32 비트 인 반면 uint64_t항상 64 비트입니다. 그러나 컴파일러가 기본 데이터 형식을 확인하도록하려면 64 비트 시스템에서 데이터 형식을 구분할 수 없었습니다.

공공-민간

이 예제는 OP의 유스 케이스에서 의미가 없다고 생각하지만 여기에서 일반적으로 문제를 해결하고 있으므로 다음을 고려하십시오.

class MultiCmdQueueCallback {
private:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

class PlcMsgFactoryImplCallback {
public:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

나는 컴파일러가 처리해야이 경우 생각 PlcNetwork::NetworkPacket으로 PlcMsgFactoryImplCallback::NetworkPacket는 다른 choises이 없기 때문에. 왜 여전히 그렇게 거부하고 모호함에 대한 책임은 나에게 미안합니다.


"왜 그래도 거부하고 모호함에 대한 책임은 나에게 미안합니다." C ++에서는 이름 조회 (가시성)가 액세스 검사보다 우선합니다. IIRC, 나는 개인에서 공개로 이름을 변경해도 기존 코드가 깨지지 않아야한다는 이론적 근거를 읽었지만 완전히 확신 할 수는 없습니다.
LF
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.