함수가 정의 된 유형에 대해서만 함수 템플릿 내에서 함수 실행


13

입력 할 때 다양한 유형을 취하는 함수 템플릿이 있습니다. 이러한 유형 중 하나만 getInt()기능이 있습니다. 따라서 코드가 해당 유형에 대해서만 함수를 실행하기를 원합니다. 해결책을 제안하십시오. 감사

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

문제와 관련이 없지만 type_info구조에는 평등 비교 연산자 가 있으므로 typeid(T) == typeid(X)작동해야합니다.
일부 프로그래머 친구

5
if constexpr조건과 함께 사용하십시오 is_same_v<T,X>.
rafix07

이것에 대한 해결책은 올해 말에 Concepts를 통해 공식적으로 더욱 우아해질 것입니다. 지금은 큰 도움이되지 않습니다.
sweenish

문제를 해결하는 방법에는 여러 가지가 있습니다. 위에서 언급 한 부부. 다른 변형의 특성 을 사용 하여 유형에 호출 가능한 getInt멤버 가 있는지 확인할 수도 있습니다 . stackoverflow.com에는 구조 또는 클래스에 특정 멤버 함수가 있는지 확인하는 방법에 대한 몇 가지 질문이 있습니다.
일부 프로그래머 친구

답변:


10

f함수 멤버 getInt가 아닌 모든 멤버에 대해 함수를 호출하려는 경우 함수에 대해 X2 개의 오버로드를 선언 할 수 있습니다 f.

  1. getInt클래스를 포함하여 멤버 함수 가있는 유형의 경우X

  2. class를 포함한 다른 모든 유형의 경우 Y.

C ++ 11 / C ++ 17 솔루션

이를 염두에두고 다음과 같이 할 수 있습니다.

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

실시간으로 확인하십시오 .

참고하시기 바랍니다 std::void_t당신이 C ++ 11로 제한하는 경우 C ++ (17)에 도입되어 있지만은, 그것은 구현하기 정말 쉽게 void_t자신에 :

template <typename...>
using void_t = void;

그리고 여기에 C ++ 11 버전이 있습니다 .

C ++ 20에는 무엇이 있습니까?

C ++ 20은 많은 장점을 제공하며 그중 하나는 개념 입니다. C ++ 11 / C ++ 14 / C ++ 17에 유효한 것은 C ++ 20에서 크게 줄일 수 있습니다.

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

실시간으로 확인하십시오 .


C ++ 17 이전에는 그 구현으로 void_t인해 일부 이전 컴파일러에 문제가 발생합니다 (링크로 지적 됨).
Jarod42

두 개의 과부하를 작성할 필요는 없습니다 ( "can"으로 "need"를 바꾸면 훨씬 더 좋습니다)
idclev 463035818

@ idclev463035818가 업데이트되었습니다. 감사합니다
NutCracker

1
업데이트 @SSAnne
호두 까기 인형

1
개념 정의가 정확하지 않습니다. 결과를 int에 할당하므로 개념은 다음과 같아야합니다.template<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui

8

if constexprC ++ 17에서 사용할 수 있습니다 .

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

이전에는 과부하 및 SFINAE 또는 태그 디스패치를 ​​사용해야합니다.


if constexprC ++ 17 기능입니다.
Andrey Semashev

그러나 이것은 수업에만 적용됩니다X
NutCracker

질문은 이제 C ++ 11 / C ++ 14
NutCracker

@ NutCracker : 태그 / 질문을 업데이트하고 기존 답변을 무효화하는 것은 좋지 않습니다 ... (경고가 괜찮더라도).
Jarod42

방금 태그를 업데이트했습니다. 질문 제목이 OP
NutCracker에

7

간단하고 과부하를 유지하십시오. 적어도 C ++ 98부터 일해 왔습니다 ...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

기능이있는 유형이 하나만 있으면 충분 getInt합니다. 더 있으면 더 이상 간단하지 않습니다. 여러 가지 방법이 있습니다. 여기에 하나가 있습니다.

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

진단 출력이 포함 된 라이브 예.


1
이 경우 이는 하나의 오버로드 ( X) 만 있기 때문에 작동 하지만, getInt앞으로 멤버와 유사한 유형이 더 많을 경우 이는 좋은 방법이 아닙니다. 당신은 아마주의 할
호두 까기 인형

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