이 전환 연산자의 과부하가 선택된 이유는 무엇입니까?


27

다음 코드를 고려하십시오 .

struct any
{
    template <typename T>
    operator T &&() const;

    template <typename T>
    operator T &() const;
};
int main()
{
    int a = any{};
}

여기서 두 번째 변환 연산자는 과부하 해상도에 의해 선택됩니다. 왜?

지금까지 내가 그것을 이해, 두 사업자에 추론된다 operator int &&() constoperator int &() const각각. 둘 다 실행 가능한 기능 세트에 있습니다. [over.match.best]를 읽는 것이 후자가 더 나은 이유를 알아내는 데 도움이되지 않았습니다.

후자가 전자보다 더 나은 기능을하는 이유는 무엇입니까?


나는 간단한 고양이이고 다른 사람의 소개가 C ++ 11의 주요 변경 사항 일 것입니다. 어딘가에 표준이 될 것입니다. 좋은 질문이지만 공감대가 있습니다. (주의 전문가 :이 의견이 hogwash이라면 알려주세요!)
Bathsheba

3
FWIW, template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;첫 번째 전화를 받도록 해
NathanOliver

5
@LightnessRaceswithMonica 변환 연산자를 사용하면 여러 변환 연산자를 사용할 수 없습니다.
NathanOliver

1
@Bathsheba 나는 그것이 이유가 아니라고 생각합니다. 마치 이동 해상도는 변경 사항을 깨뜨릴 수 있기 때문에 오버로드 확인으로 절대 선택할 수 없다는 말과 같습니다. 이동 생성자를 작성하는 경우 해당 정의에 의해 코드가 "손상"되는 것으로 선택합니다.
Brian

1
@LightnessRaceswithMonica 내 머리 속에 똑바로 유지하는 방법은 반환 유형을 함수의 이름으로 취급하는 것입니다. 다른 유형이 동일 다른 이름을 동일 성공 :)
NathanOliver

답변:


13

반환 T&하는 변환 연산자는 반환하는 변환 연산자보다 더 전문적이기 때문에 선호 T&&됩니다.

C ++ 17 [temp.deduct.partial] / (3.2) 참조 :

변환 함수 호출과 관련하여 변환 함수 템플리트의 리턴 유형이 사용됩니다.

및 / 9 :

주어진 타입, 공제 양방향 성공하면 모두 (즉, 종류가 상기 변환 후 동일) PA(앞서 언급 된 유형으로 교체되기 전에) 참조 타입과 같다 : - 만약 인수 템플릿 유형 lvalue 참조이고 매개 변수 템플리트의 유형이 아닌 경우, 매개 변수 유형은 최소한 인수 유형만큼 특화된 것으로 간주되지 않습니다. ...


12

추론 된 반환 값 변환 연산자는 약간 이상합니다. 그러나 핵심 아이디어는 사용되는 매개 변수를 선택하는 함수 매개 변수처럼 작동한다는 것입니다.

그리고 사이에 결정할 때 T&&와 과부하 해상도 규칙에서 승리. 이것은 다음을 허용하는 것입니다.T&T&

template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }

일하다. T&&lvalue와 일치 할 수 있지만 lvalue와 범용 참조 과부하가 모두 사용 가능한 경우 lvalue가 선호됩니다.

올바른 변환 연산자 세트는 다음과 같습니다.

template <typename T>
operator T&&() &&;

template <typename T>
operator T &() const; // maybe &

또는

template <typename T>
operator T() &&;

template <typename T>
operator T &() const; // maybe &

수명 연장에 실패한 것을 방지합니다.

3 순서를 결정하는 데 사용되는 유형은 부분 순서가 수행되는 컨텍스트에 따라 다릅니다.

[한조각]

(3.2) 변환 함수 호출과 관련하여 변환 함수 템플릿의 반환 유형이 사용됩니다.

그러면 과부하를 선택할 때 "보다 전문화 된"규칙에 따라 종료됩니다.

(9.1) 인수 템플리트의 유형이 lvalue 참조이고 매개 변수 템플리트의 유형이 아닌 경우, 매개 변수 유형은 최소한 인수 유형만큼 전문화 된 것으로 간주되지 않습니다. 그렇지 않으면,

따라서 operator T&&최소한으로 전문되지 않는 operator T&한편의 규정 상태, operator T&적어도으로 전문되지 않기 operator T&&때문에, operator T&보다 더 전문operator T&& 됩니다.

보다 전문화 된 템플릿은 오버로드 해상도를 낮추고 나머지는 동일합니다.


내가 대답 할 때 누군가 언어 변호사 태그를 추가했습니다 . 간단하게 삭제할 수 있습니다. 나는 호출 된 것을 선택하는 매개 변수로 취급된다는 텍스트와 템플릿 과부하를 선택할 때 &&vs &가 처리되는 방식에 대해 말하는 텍스트를 읽는 기억이 있습니다 . 그러나 정확한 텍스트를 괴롭히는 데는 시간이 걸립니다. .
Yakk-Adam Nevraumont

4

int에서 를 초기화하려고 합니다 any. 그것을위한 과정 :

  1. 우리가 할 수있는 모든 방법을 알아 내십시오. 즉, 모든 후보자를 결정하십시오. 이들은 int표준 변환 시퀀스 ( [over.match.conv] ) 를 통해 변환 할 수있는 비명 시적 변환 함수에서 비롯 됩니다. 이 섹션에는 다음과 같은 문구가 포함되어 있습니다.

    "참조"를 반환하는 변환 함수에 대한 호출 X은 유형의 glvalue X이므로 이러한 변환 함수는 X후보 함수를 선택하는이 프로세스에 대해 산출 되는 것으로 간주됩니다 .

  2. 최고의 후보자를 선택하십시오.

1 단계 후에는 두 명의 후보가 있습니다. operator int&() const그리고 후보 기능을 선택하기위한 목적으로 operator int&&() const산출되는 것으로 간주되는 둘 다 int. 가장 좋은 후보 산출량 은 어느 것 int입니까?

rvalue 참조보다 lvalue 참조를 선호하는 순위 결정이 있습니다 ( [over.ics.rank] /3.2.3 ) 있습니다. 우리는 실제로 여기에 참조를 바인딩하지 않으며 예제는 약간 거꾸로되어 있습니다-이것은 매개 변수가 lvalue 대 rvalue 참조 인 경우입니다.

해당 사항이 적용되지 않으면 보다 전문화 된 기능 템플릿을 선호하는 [over.match.best] /2.5 순위 결정에 해당합니다.

일반적으로 경험에 의하면 일반적으로 더 구체적인 전환이 가장 적합합니다. lvalue 참조 변환 함수는 전달 참조 변환 함수보다 더 구체적이므로 선호됩니다. intrvalue를 요구하는 초기화하는 것에 대해서는 아무것도 없습니다 (대신에 초기화 int&&하고 operator T&() const있었다면 후보가 아니었을 것입니다).


3.2.3 실제로 T&&승은 그렇지 않습니까? 아, 그러나 우리는 rvalue가 구속되어 있지 않습니다. 아니면 우리? 우리의 후보를 따기 때 우리가 가진 방법을 몇 가지 단어를 정의해야 int&하고 int&&, 바인딩이었다?
Yakk-Adam Nevraumont

@ Yakk-AdamNevraumont 아니요, rvalue 참조보다 lvalue 참조를 선호합니다. 어쨌든 그것은 잘못된 섹션 일 것으로 생각되며 "보다 전문화 된"순위 결정이 올바른 것입니다.
Barry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.