암시 적 인수 변환에 의존하는 것이 위험한 것으로 간주됩니까?


10

C ++에는 인수 유형이 예상되는 것이 아닌 경우 매개 변수 유형의 일치하는 생성자를 자동으로 호출하는 기능 (적절한 이름을 알 수 없음)이 있습니다.

이것의 가장 기본적인 예 std::stringconst char*인수 를 기대하는 함수를 호출하는 것입니다 . 컴파일러는 자동으로 적절한 std::string생성자 를 호출하는 코드를 생성합니다 .

궁금합니다. 생각보다 가독성이 좋지 않습니까?

예를 들면 다음과 같습니다.

class Texture {
public:
    Texture(const std::string& imageFile);
};

class Renderer {
public:
    void Draw(const Texture& texture);
};

Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);

괜찮아? 아니면 너무 멀어요? 하지 말아야 할 경우 Clang 또는 GCC가 경고 할 수 있습니까?


1
Draw가 나중에 문자열 버전으로 오버로드되면 어떻게 될까요?
ratchet freak

1
@Dave Rager의 답변에 따르면 이것이 모든 컴파일러에서 컴파일 될 것이라고 생각하지 않습니다. 그의 답변에 대한 내 의견을 참조하십시오. 분명히 C ++ 표준에 따르면 이와 같은 암시 적 변환을 연결할 수 없습니다. 한 번만 변환 할 수 있습니다.
Jonathan Henson

죄송합니다. 실제로 컴파일하지 않았습니다. 예제를 업데이트했는데 여전히 끔찍한 IMO입니다.
futlib

답변:


24

이를 변환 생성자 (또는 암시 적 생성자 또는 암시 적 변환)라고합니다.

컴파일 타임 스위치가 발생했을 때 경고하는 것을 알지 못하지만 매우 쉽게 방지 할 수 있습니다. explicit키워드 만 사용하십시오 .

class Texture {
public:
    explicit Texture(const std::string& imageFile);
};

변환 생성자가 좋은 아이디어인지 여부에 따라 다릅니다.

암시 적 변환이 의미가있는 상황 :

  • 이 클래스는 암시 적으로 생성 된 경우 신경 쓰지 않아도 될 정도로 저렴합니다.
  • 일부 클래스는 개념적으로 인수와 암시 적으로 유사하므로 ( 암시 적으로 변환 할 수 std::string있는 것과 동일한 개념을 반영하는 등 const char *) 암시 적 변환이 의미가 있습니다.
  • 암시 적 변환이 비활성화 된 경우 일부 클래스는 사용하기가 훨씬 불쾌 해집니다. 문자열 리터럴을 전달할 때마다 std :: string을 명시 적으로 호출해야합니다. Boost의 부분은 비슷합니다.

암시 적 변환이 이해가되지 않는 상황 :

  • 구성이 비쌉니다 (예 : 텍스처 파일, 그래픽 파일로드 및 파싱 필요).
  • 클래스는 개념적으로 그들의 주장과 매우 ​​유사합니다. 예를 들어, 크기를 인수로 사용하는 배열과 유사한 컨테이너를 고려하십시오.
    FlagList 클래스
    {
        FlagList (int initial_size); 
    };

    무효 SetFlags (const FlagList & flag_list);

    int main () {
        // 전혀 명확하지는 않지만 컴파일
        //하고있는 일.
        SetFlags (42);
    }
  • 건축에는 원하지 않는 부작용이있을 수 있습니다. 예를 들어, 유니 코드에서 ANSI 로의 변환은 정보를 잃을 수 있으므로 AnsiString클래스는 암시 적으로을 구성 해서는 안됩니다UnicodeString .

더 읽을 거리 :


3

이것은 답변보다 더 많은 의견이지만 댓글을 작성하기에는 너무 큽니다.

흥미롭게도 g++내가 그렇게하지 못하게하십시오.

#include <iostream>
#include <string>

class Texture {
        public:
                Texture(const std::string& imageFile)
                {
                        std::cout << "Texture()" << std::endl;
                }
};

class Renderer {
        public:
                void Draw(const Texture& texture)
                {
                        std::cout << "Renderer.Draw()" << std::endl;
                }
};

int main(int argc, char* argv[])
{
        Renderer renderer;
        renderer.Draw("foo.png");

        return 0;
}

다음을 생성합니다.

$ g++ -o Conversion.exe Conversion.cpp 
Conversion.cpp: In function int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to Renderer::Draw(const char [8])’
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)

그러나 줄을 다음과 같이 변경하면

   renderer.Draw(std::string("foo.png"));

해당 변환을 수행합니다.


그것은 실제로 g ++에서 흥미로운 "기능"입니다. 올바른 코드를 생성하기 위해 컴파일 타임에 가능한 한 재귀 적으로 내려 가지 않고 하나의 유형을 깊게 검사하는 버그이거나 g ++ 명령에 설정 해야하는 플래그가 있다고 가정합니다.
Jonathan Henson

1
en.cppreference.com/w/cpp/language/implicit_cast g ++이 표준을 엄격하게 준수 하는 것 같습니다. OP의 코드에 너무 관대 한 것은 Microsoft 또는 Mac의 컴파일러입니다. "생성자 또는 사용자 정의 변환 함수에 대한 인수를 고려할 때 하나의 표준 변환 시퀀스 만 허용됩니다 (그렇지 않으면 사용자 정의 변환이 효과적으로 연결될 수 있음)"
Jonathan Henson

예, 방금 gcc컴파일러 옵션 중 일부를 테스트하기 위해 코드를 함께 던졌습니다 (이 특정 사례를 해결할 수있는 것처럼 보이지는 않습니다). 나는 그것에 대해 더 자세히 보지 않았지만 (나는 작동해야합니다 :-) 그러나 gcc표준을 준수하고 explicit키워드를 사용하면 컴파일러 옵션이 불필요하다고 간주되었습니다.
Dave Rager

암시 적 변환은 연결되어 있지 않으며 Texture암시 적으로 (다른 답변의 지침에 따라) 암시 적으로 구성해서는 안되므로 더 나은 콜 사이트가 될 것입니다 renderer.Draw(Texture("foo.png"));(예상대로 작동한다고 가정).
Blaisorblade

3

이를 암시 적 유형 변환이라고합니다. 일반적으로 불필요한 반복을 막기 때문에 좋습니다. 예를 들어, 추가 코드를 작성하지 않아도 자동으로 std::string버전을 얻 Draw습니다. 또한 개방형 원칙을 따르는 데 도움이 될 수 있습니다. 개방형 원칙 Renderer을 수정하지 않고도 기능 을 확장 할 수 있기 때문 Renderer입니다.

반면에 단점이 없습니다. 한 가지 이유는 논쟁이 어디에서 오는지 알아내는 것을 어렵게 만들 수 있습니다. 때로는 다른 경우에 예기치 않은 결과가 발생할 수 있습니다. 이것이 바로 explicit키워드입니다. Texture생성자 에 배치하면 암시 적 유형 변환에 해당 생성자를 사용하지 않습니다. 암시 적 유형 변환에 대해 전역 적으로 경고하는 방법을 알지 못하지만 이것이 방법이 존재하지 않는다는 의미는 아니며 gcc에만 이해할 수 없을 정도로 많은 옵션이 있습니다.

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