const std :: string & 사용하는 함수가 0을 허용하지 않도록 방지


97

천 단어의 가치 :

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        std::cout<<s<<"\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

문자열을 수락하는 대괄호 연산자에 숫자 0을 전달할 때 컴파일러가 불평하지 않습니다. 대신, 다음을 사용하여 메소드에 들어가기 전에 컴파일되고 실패합니다.

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

참고로 :

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

내 추측

컴파일러는 암시 적으로 std::string(0)생성자를 사용하여 메소드를 입력하므로 아무런 이유없이 동일한 문제 (Google 위의 오류)가 발생합니다.

질문

어쨌든 클래스 측 에서이 문제를 해결할 수 있습니까? 그래서 API 사용자는 이것을 느끼지 않으며 컴파일 타임에 오류가 감지됩니까?

즉, 과부하 추가

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

좋은 해결책이 아닙니다.


2
"0xC0000005 : 액세스 위반 읽기 위치 0x00000000"과 함께 ohNo [0]의 Visual Studio에서 예외가 발생하여 컴파일 된 코드
TruthSeeker

5
인수 의 개인 오버로드를 선언하고 인수 operator[]()를 허용 int하지 마십시오.
피터

2
@Peter 링커 오류 라는 점에 유의하지만 여전히 내가 가진 것보다 낫습니다.
kabanus

5
@kabanus 위 시나리오 에서 연산자는 개인이기 때문에 컴파일러 오류가 발생합니다! 클래스 내에서 호출 된 경우에만 링커 오류 ...
Aconcagua

5
즉, 어떤 C ++ (11)를 사용할 수없는 시나리오에서 특히 흥미로운 @ 피터 - 이들은 않는 오늘날에도 존재한다 (실제로는 내가 처리해야하는 프로젝트에있어, 나는 거의 몇 가지의 새로운 기능을 누락 ... ).
Aconcagua

답변:


161

이유 std::string(0)는 유효 0하며 널 포인터 상수 이기 때문 입니다. 따라서 0은 포인터를 취하는 문자열 생성자와 일치합니다. 그런 다음 코드는 null 포인터를 전달할 수 없다는 전제 조건을 무시하고 실행됩니다 std::string.

0런타임 값이 리터럴 인 경우 리터럴 만 null 포인터 상수로 해석됩니다 int(오버로드 해상도가 int대신 변환을 찾기 때문에 ). 널 포인터 상수가 아니기 1때문에 문자 그대로 문제 도 아닙니다 1.

컴파일 시간 문제 (문자 그대로 유효하지 않은 값)이므로 컴파일 타임에 잡을 수 있습니다. 이 형식의 과부하를 추가하십시오.

void operator[](std::nullptr_t) = delete;

std::nullptr_t의 유형입니다 nullptr. 그리고 그것은 일치 어떤 이라고해도, 널 포인터 상수를 0, 0ULL또는 nullptr. 그리고 함수가 삭제되었으므로 과부하 해결 중에 컴파일 시간 오류가 발생합니다.


이것은 지금까지 가장 좋은 해결책이며 NULL 포인터를 오버로드 할 수 있다는 것을 완전히 잊어 버렸습니다.
kabanus

Visual Studio에서는 "ohNo [0]"도 null 값 예외를 발생시킵니다. std :: string 클래스에 특정한 구현을 의미합니까?
TruthSeeker

@pmp 던져지는 것은 (구체적으로) 구현에 따라 다르지만 요점은 문자열이 모두 NULL 포인터라는 것입니다. 이 솔루션을 사용하면 예외 부분에 도달하지 않으며 컴파일 타임에 감지됩니다.
kabanus

18
@pmp- std::stringC ++ 표준에서는의 생성자에 null 포인터를 전달할 수 없습니다. 정의되지 않은 동작이므로 MSVC는 원하는대로 처리 할 수 ​​있습니다 (예외 발생).
StoryTeller-Unslander Monica

26

하나의 옵션은 privateoperator[]() 필수 인수를 허용하는 오버로드 정의하지 않는 것입니다.

이 옵션은 다음과 같은 옵션과 달리 모든 C ++ 표준 (1998 이상)에서 작동합니다. void operator[](std::nullptr_t) = delete C ++ 11의 유효한 합니다.

제작 operator[]()private멤버 당신의 예에서 진단 가능한 오류가 발생합니다 ohNo[0] 그 표현이 멤버 함수 또는 사용하지 않는 한, friend클래스의.

해당 표현식이 멤버 함수 또는 friend클래스에서 사용되는 경우 코드가 컴파일되지만 함수가 정의되지 않았기 때문에 일반적으로 빌드가 실패합니다 (예 : 정의되지 않은 함수로 인한 링커 오류).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.