함수 템플릿의 기본 템플릿 인수


187

기본 템플릿 인수가 클래스 템플릿에서만 허용되는 이유는 무엇입니까? 멤버 함수 템플릿에서 기본 유형을 정의 할 수없는 이유는 무엇입니까? 예를 들면 다음과 같습니다.

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

대신 C ++은 기본 템플릿 인수가 클래스 템플릿에서만 허용되도록합니다.


8
+1 정말 까다로운 질문입니다.
AraK

1
처음 세 개의 답변에 대해서는 다음 예제를 고려하십시오 struct S { template <class R = int> R get_me_R() { return R(); } };. 템플리트 매개 변수는 컨텍스트에서 추론 할 수 없습니다.
AraK

3
좋은 질문. 3 명이 이미 "이치가 없다"고 대답했으며, 모두 잘못되었다. 함수 템플릿 매개 변수가 함수 호출 매개 변수에서 항상 공제되는 것은 아닙니다. 예를 들어, 허용 된 경우을 쓴 template <int N = 1> int &increment(int &i) { i += N; return i; }다음 increment(i);또는을 쓸 수 increment<2>(i);있습니다. 그대로 작성해야합니다 increment<1>(i);.
Steve Jessop

실제로, 내 것과 AraK의 예제는 모두 과부하로 처리 할 수 ​​있습니다. 템플릿 매개 변수 추론되거나 지정 수 있기 때문에 litb는 생각할 수 없습니다 .
Steve Jessop

3
@Steve : 누락 된 세미콜론은 실제로 1992 년 4 월 1 일자 Journal of Object-Oriented Programming에 게시 된 B. Stavtrup의 "C ++ 공백의 오버로드"를 보완하기위한 새로운 EOL 연산자 과부하입니다 ( www2.research.att.com/~bs/ papers.html )

답변:


148

기본 템플릿 인수를 제공하는 것이 좋습니다. 예를 들어 정렬 함수를 만들 수 있습니다.

template<typename Iterator, 
         typename Comp = std::less<
            typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
  ...
}

C ++ 0x는 C ++을 소개합니다. Bjarne Stroustrup의이 결함 보고서 : 함수 템플릿에 대한 기본 템플릿 인수 와 그가 말한 내용 참조

함수 템플릿에 대한 기본 템플릿 인수의 금지는 독립형 함수가 2 급 시민으로 취급되어 모든 템플릿 인수가 지정된 함수가 아닌 함수 인수에서 추론되는 시간을 잘못 인식 한 것입니다.

이 제한은 멤버 함수와 다른 독립형 함수를 불필요하게 만들어 STL 스타일 코드를 작성하는 것을 어렵게하여 프로그래밍 스타일을 심각하게 제한합니다.


@Arman, 결함 보고서 링크에는 C ++ 0x 작업 초안의 변경 사항 및 토론이 포함되어 있습니다. 추론되거나 명시 적으로 지정되지 않은 인수는 기본 인수에서 얻습니다. GCC4.4는 C ++ 0x 모드에서 함수 템플릿에 대한 기본 인수를 지원합니다.
Johannes Schaub-litb

4
질문이나 답변과는 아무런 관련이 없지만 Herb Sutter는 지난 토요일 회의 후에 다가오는 표준 C ++ 11을 불렀습니다. 난 그냥 오늘 읽고 공유 느낌 :) herbsutter.wordpress.com/2010/03/13/…
David Rodríguez-dribeas

그리고 의무 후속 질문 ... 언제 다른 컴파일러로 들어갈 것으로 예상됩니까 :)
Jamie Cook

@ JohannesSchaub-litb 같은 문제가있었습니다 : 템플릿 함수에서 기본 유형을 매운 수 없습니다. 기본 유형 ( double필자의 경우)에서 명시 적으로 함수를 인스턴스화하여 해결했습니다 . 아마도 이것은 "일반적인"것이 아니지만,이 관행에 단점이 있습니까? 감사.
JackOLantern

다음 코드는 error: invalid conversion from ‘int’ to ‘int*’이유 와 같은 오류와 함께 컴파일에 실패합니다 .`#include <array> #include <algorithm> #include <functional> template <typename Iterator, typename Comp = std :: less <Iterator>> void my_sort ( 반복자 구걸, 반복자 끝, Comp c = Comp ()) {std :: sort (beg, end, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); }`
Luke Peterson

36

C ++ 템플릿 을 인용하려면 : 완전한 안내서 (207 페이지) :

템플릿이 원래 C ++ 언어에 추가되었을 때, 명시 적 함수 템플릿 인수는 유효한 구성이 아닙니다. 함수 템플릿 인수는 항상 호출 식에서 추론 할 수 있어야했습니다. 결과적으로, 기본값은 항상 추론 된 값으로 대체되므로 기본 함수 템플리트 인수를 허용하는 강력한 이유가없는 것 같습니다.


간단하고 간결한 :)
InQusitive

17

지금까지 함수 템플릿에 대한 기본 템플릿 매개 변수의 모든 예제는 과부하로 수행 할 수 있습니다.

아라크 :

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

될 수 있습니다 :

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

내 자신의:

template <int N = 1> int &increment(int &i) { i += N; return i; }

될 수 있습니다 :

template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }

litb :

template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())

될 수 있습니다 :

template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())

template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())

Stroustrup :

template <class T, class U = double>
void f(T t = 0, U u = 0);

될 수 있습니다 :

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

다음 코드로 증명했습니다.

#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>

template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) { 
    std::stringstream ss;
    if (isprint((unsigned char)c)) {
        ss << "'" << c << "'";
    } else {
        ss << (int)c;
    }
    return ss.str();
}

template <typename S, typename T> void g(S s, T t){
    std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
        << ">(" << s << "," << prettify(t) << ")\n";
}


template <typename S, typename T> void f(S s = 0, T t = 0){
    g<S,T>(s,t);
}

template <typename S> void f(S s = 0, double t = 0) {
    g<S,double>(s, t);
}

int main() {
        f(1, 'c');         // f<int,char>(1,'c')
        f(1);              // f<int,double>(1,0)
//        f();               // error: T cannot be deduced
        f<int>();          // f<int,double>(0,0)
        f<int,char>();     // f<int,char>(0,0)
}

인쇄 된 출력은 f에 대한 각 호출의 주석과 일치하며 주석 처리 된 호출이 예상대로 컴파일되지 않습니다.

따라서 기본 템플릿 매개 변수가 "필요하지 않다"고 생각하지만 기본 함수 인수가 "필요하지 않음"과 같은 의미 일 것입니다. Stroustrup의 결함 보고서에서 알 수 있듯이, 교육되지 않은 매개 변수를 추가하는 것은 너무 늦어서 누구나 기본값을 유용하게 사용했음을 깨닫거나 인식 할 수 없었습니다. 따라서 현재 상황은 결코 표준이 아닌 함수 템플릿 버전을 기반으로합니다.


@Steve : 계란이 닭고기보다 빠르게 달렸나요? :) 흥미 롭습니다. 감사.
Arman

1
아마도 그중 하나 일 것입니다. C ++ 표준화 프로세스는 부분적으로 느리게 실행되므로 변경으로 인해 표준의 다른 곳에서 기회 나 어려움이 발생하는 경우 사람들이 알 수 있습니다. 모순이나 모호함을 발견 할 때 표준 초안을 구현하는 사람들이 어려움을 겪고 있습니다. 이전에 허용되지 않은 것들을 허용 할 수있는 기회는 더 이상 불법 일 필요가 없다는 코드를 작성하려는 사람에게 의존합니다.
Steve Jessop

2
하나 더 : template<typename T = void> int SomeFunction();. 여기서 템플릿 매개 변수는 사용되지 않으며 실제로 함수는 호출되지 않습니다. 참조 된 유일한 장소는 decltype또는 sizeof입니다. 이름은 의도적으로 다른 함수의 이름과 일치하지만 템플릿이라는 사실은 컴파일러가 자유 함수가 존재하는 경우 자유 함수를 선호한다는 것을 의미합니다. 이 두 가지는 SFINAE에서 함수 정의가 누락 된 기본 동작을 제공하는 데 사용됩니다.
Tom

4

Windows에서 모든 버전의 Visual Studio를 사용하면이 오류 ( C4519 )를 경고 로 변환 하거나 다음과 같이 비활성화 할 수 있습니다.

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

자세한 내용은 여기를 참조 하십시오 .


1
이렇게하면 "기본 템플릿 인수는 클래스 템플릿에서만 허용됩니다"메시지가 비활성화되지만 실제로 템플릿 인스턴스화 프로세스 에서 제공된 값을 사용 하지는 않습니다 . VS2013 (또는 C ++ 11 결함 226 "함수 템플릿의 기본 템플릿 인수"를 완료 한 다른 컴파일러)이 필요합니다
puetzk

1

내가 사용하는 것은 다음 트릭입니다.

다음과 같은 기능을 원한다고 가정 해 봅시다.

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

당신은 허용되지 않지만 다음 방법으로 수행합니다.

template <typename T>
struct MyArray_t {
void add(T i) 
{
    // ...
}
};

template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
    /*static - as you wish */ ARR_E* parr_;
    void doStuff(); /* do not make this one static also, MSVC complains */
};

template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
    E one(1);
    parr_->add( one );
}

따라서이 방법으로 다음과 같이 사용할 수 있습니다.

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

보시다시피 두 번째 매개 변수를 명시 적으로 설정할 필요가 없습니다. 아마도 누군가에게 유용 할 것입니다.


이것은 분명히 대답이 아닙니다.
강아지

@deadmg-왜 설명 할 수 있습니까? 우리는 모두 C ++ 템플릿 전문가가 아닙니다. 감사.
Kev

이것은 매우 깔끔한 해결 방법이지만 원하는 모든 경우를 다루지는 않습니다. 예를 들어 이것을 생성자에 어떻게 적용 하시겠습니까?
Tiberiu Savin

@TiberiuSavin-내가 당신을 올바르게 이해했다면 다음과 같이 할 수 있습니다 : template <typename E, typename ARR_E> worker <E, ARR_E> :: worker (ARR_E * parr) {parr_ = parr; }. 그리고 다음과 같이 사용하십시오 : worker <int> w2 (& my_array);
alariq
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.