2016-09-10 9 views
1

attempt to answer another question에서 CRTP 기본 클래스의 자식이 해당 생성자의 매개 변수로 특정 유형을 받아들이도록하는 구성표가 나왔습니다. 매개 변수 유형의 생성자를 private으로 만들고 CRTP 기본 클래스를 friend으로 지정하고 선언합니다. 기본 클래스 생성자에 대한 매개 변수로 매개 변수 유형.CRTP 상속의 기본 클래스가 "friending"해도 하위 클래스에도 영향을 줍니까?

template <typename T> 
class SingletonBase { 
    protected: class P { friend class SingletonBase<T>; P() = default; }; 
    public: 
    SingletonBase(P) {} 
}; 

class Logger: public SingletonBase<Logger> { 
    using BASE = SingletonBase<Logger>; 
    public: 
    Logger() : BASE{P{}} {} // WHY NO ACCESS VIOLATION? 
}; 

:이 방식은 액세스 위반을 통해 원하는 보호를 제공하는 것을 증명하려고 할 때

그러나, 나는 매개 변수 유형의 생성자는 민간에도 불구하고, 자식 클래스를 구축 할 수 있었다 발견 이 compiles without error, 액세스 위반이 예상 되더라도. 왜?

답변

2

CRTP 상속의 기본 클래스를 "friending"해도 하위 클래스에도 영향을 줍니까?

아니요, 물론 아닙니다. 우정은 계승되지 않습니다. 문제를 설명하려면

먼저 P::P()이 기본값 인 기본 생성자이며 trivial default constructor입니다.

둘째 P{}이다 value initialization (사람 C++ 11)

T는 기본 생성자 클래스 타입 인 경우 (강조 광산)

2)도 사용자 제공 아니다 (즉, 암시 적으로 정의되거나 기본 설정된 기본 생성자가있는 클래스 일 수 있습니다.) 개체가 0으로 초기화 된 다음 기본값이 아닌 기본 생성자 인이있는 경우 기본 초기화됩니다.

여기는 default initializated이 아니라 zero initialized입니다.P의 개인 기본 생성자는 전혀 호출되지 않습니다. T는 비 연합 클래스 타입 인 경우

모든 기본 클래스 및 비 - 정적 데이터 멤버는 영으로 초기화되고 모든 패딩 비트 제로로 초기화된다. 생성자 (있는 경우)는 무시됩니다.

명시 적으로 기본 초기화로 변경하면 액세스 위반 오류가 발생합니다.

Logger() : BASE{P()} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P 
//    ~~ 

단순화 된 데모

class X { X() = default; }; 

int main() 
{ 
    X x1{}; // fine 
    X x2; // error: calling a private constructor of class 'X' 
} 

LIVE

솔루션

가 아닌 사소한 생성자 사용자 정의 기본 생성자를 제공 할 수

에 값 초기화 동작을 변경하십시오.

template <typename T> 
class SingletonBase { 
    protected: 
    class P { 
     friend class SingletonBase<T>; 
     P() {} // user-defined default constructor 
    }; 
    public: 
    SingletonBase(P) {} 
}; 

class Logger: public SingletonBase<Logger> { 
    using BASE = SingletonBase<Logger>; 
    public: 
    Logger() : BASE{P{}} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P' 
}; 
+0

간단히 말해'= default'를 사용하면 "private constructor"트릭에 대한 액세스 지정자 허점을 제공하는 것처럼 들립니다. '= default'에서'{}'*로 바꾸면 클래스 *가 친구가 아닌 다른 사람에 의해 처음부터 만들어 질 수 없습니까? –

+0

@KyleStrand 예. 요점은 사용자 정의 생성자가 아닌 값 초기화의 동작을 변경하지 않는 것이 중요합니다. – songyuanyao

+0

해결책으로 추가한다면 ('friend'이 아닌 클래스가 객체의 인스턴스를 생성하지 못하게하는 것이 목표이기 때문에) 그 대답을 받아 들일 것입니다. –

0

당신이 한 일은 당신의 friend 성명서와는 아무런 관련이 없습니다!

friend을 제거하면 코드도 잘 컴파일됩니다!

빈 클래스의 기본 생성자가 공개되어 있기 때문이다

:

에서 C++ 11 표준 :

X 클래스에는 사용자가 선언 된 생성자가없는 경우 생성자가 필요 없음 매개 변수는 암시 적으로 기본값으로 선언됩니다. 암시 적으로 선언 된 기본 생성자는 해당 클래스의 인라인 공용 멤버입니다.

이 같은 디폴트 생성자가없는 경우 :

template <typename T> 
class SingletonBase 
{ 
    protected: 
     class P 
     { 
      friend class SingletonBase<T>; 
      P(int){ } 
     }; 

    public: 
     SingletonBase(P) {} 
}; 

class Logger: public SingletonBase<Logger> 
{ 
    using BASE = SingletonBase<Logger>; 

    public: 
    Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION? 
}; 

당신은 "액세스"위반을 얻을 것이다 그리고 당신은 당신의 friend 작동하지 않았다 볼! :

main.cpp: In constructor 'Logger::Logger()': 
main.cpp:10:17: error: 'SingletonBase<T>::P::P(int) [with T = Logger]' is private 
       P(int){ } 
       ^
main.cpp:22:28: error: within this context 
     Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION? 
+0

...하지만 명시 적으로 생성자'default' **를 선언하는 것은 ** 생성자를 선언하는 것입니다. 나는 "사용자가 선언하지 않은 생성자"로 간주하지 않는다고 생각했다. 사실, 나는 그들이 "no user - ** defined ** constructor"라고 말하는 대신에 그렇게 말한 것이라고 생각했다. –