SFINAE(Substitution failure is not an error)

Substitution failure is not an error 알아보기

Posted by Start Bootstrap on September 05, 2023

SFINAE(Substitution failure is not an error)

SFINAE(Substitution failure is not an error)는 문장 그대로 대체 실패는 오류가 아니다 라는 의미입니다. C++에서 template은 Compiler가 사용처를 발견해서 적용이 되는 새로운 type의 함수 등을 추론해서 생성합니다. 이 과정에서 해당 template의 매개변수에 형이나 값을 넣을 수 없어도 오류가 발생하지 않는 상황을 의미합니다. 이는 template 함수 오버로딩에서 주로 발견되며 boost::enable_if에서 제공하는 코드를 참조해서 한번 직접 실행해서 확인해보겠습니다.

        
            int negativeSomething( const float value ) # 1
            { 
                std::cout << "int negativeSomething function call" << std::endl;
                return -( value + 100 ); 
            }

            template 
            typename F::result_type negativeSomething( const F& value ) # 2
            { 
                std::cout << "template negative function call" << std::endl;
                return -( value + 100 );
            }

            int main( void )
            {
                const int value = 1;
                int result = negativeSomething( value );
                std::cout << result << std::endl;
                return 0;
            }
        
    

boost document에 따르면 # 1의 정의에 맞는 함수가 있다 하더라도 컴파일러는 추론을 위해 두 정의를 모두 고려해서 프로토 타입을 결정한다는 의미입니다. 따라서 컴파일러는 아래와 같은 코드를 고려할 수 있다고 합니다

        
            int::result_type negativeSomething( const int& value )
            { 
                std::cout << "template negative function call" << std::endl;
                return -( value + 100 );
            }
        
    

물론 결과는 더 적합한 #1 함수가 실행되어서 int negativeSomething function call 문장과 -101 값이 출력이 되었습니다. 이번엔 반대로 적합하지 않은 방식으로 테스트 해보겠습니다.

        
            template 
            T negativeSomething( const T& value ) # 1
            { 
                std::cout << "template negative function call" << std::endl;
                return -value;
            }
                
                
            int main( void )
            {
                    
                std::cout << negativeSomething( "haha" ) << std::endl;
                return 0;
            }
        
    

해당 코드는 # 1의 함수는 input인 const char[]에 적합한 처리를 할 수 없기 때문에 컴파일 에러가 발생합니다. 하지만 그 함수를 원래대로 두고 함수를 하나 추가하겠습니다.

        
            template 
            T negativeSomething( const T& value ) # 1
            { 
                std::cout << "template negative function call" << std::endl;
                return -value;
            }

            const std::string& negativeSomething( const std::string& value ) # 2
            { 
                std::cout << "string negativeSomething function call" << std::endl;
                return value;
            }
                
            int main( void )
            {
                    
                std::cout << negativeSomething( "haha" ) << std::endl;
                return 0;
            }
        
    

해당 코드는 # 1의 함수는 input인 const char[]에 적합한 처리를 할 수 없기 때문에 컴파일 에러가 발생해야 하지만 위의 설명대로 적합한 # 2의 함수를 발견해서 SFINAE 원칙으로 인해 잘못된 처리로 성립되지 않습니다. 단순히 컴파일러가 변환에 적절한 집합으로 여기지 않기 때문에 문제가 없이 넘어갑니다.

std::enable_if

SFINAE의 방식을 사용할 수 있도록 도와주는 표준은 std::enable_if 기능이 있습니다.