関数テンプレートのデフォルトのテンプレート引数


187

デフォルトのテンプレート引数がクラステンプレートでのみ許可されているのはなぜですか?メンバー関数テンプレートでデフォルトの型を定義できないのはなぜですか?例えば:

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

代わりに、C ++は、デフォルトのテンプレート引数がクラステンプレートでのみ許可されることを強制します。


8
+1それは本当に難しい問題です。
AraK

1
投稿された最初の3つの回答について、次の例を検討してstruct S { template <class R = int> R get_me_R() { return R(); } };ください:テンプレートパラメータはコンテキストから推定できません。
AraK 2010年

3
良い質問。3人はすでに「意味がない」と答えており、一般的には全員が間違っています。関数テンプレートのパラメーターは、関数呼び出しパラメーターから常に控除できるわけではありません。たとえば、許可されている場合はtemplate <int N = 1> int &increment(int &i) { i += N; return i; }、を記述してから、increment(i);またはを記述できますincrement<2>(i);。そのまま書いてみますincrement<1>(i);
スティーブジェソップ

実際、私のものとAraKの例はどちらもオーバーロードで処理できます。litbはできません。テンプレートパラメータ推定されるか、指定される可能性があるためです。
スティーブジェソップ

3
@Steve:欠落しているセミコロンは、B。Stavtrupの "Overloading of C ++ Whitespace"がJournal of Object-Oriented Programming、1992年4月1日で公開された新しい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によるこの欠陥レポートを参照してください:関数テンプレートのデフォルトのテンプレート引数と彼の発言

関数テンプレートのデフォルトテンプレート引数の禁止は、自立型関数がセカンドクラスシチズンとして扱われ、すべてのテンプレート引数を指定するのではなく、関数引数から推定することを要求した時代の名残です。

この制限は、自立関数をメンバー関数と不必要に異なるものにすることでプログラミングスタイルを大幅に窮屈にし、STLスタイルのコードの記述を困難にします。


@Arman、障害レポートのリンクには、C ++ 0xの作業草案およびディスカッションに加えられた変更が含まれています。推論も明示的にも指定されていない引数は、デフォルトの引数から取得されます。GCC4.4は、C ++ 0xモードの関数テンプレートのデフォルト引数をサポートしています。
Johannes Schaub-litb

4
質問や回答とは関係ありませんが、最後の土曜日の会議の後にハーブサッターが次期標準C ++ 11を呼び出しました。私はちょうど:)今日それを読んで、共有のように感じるherbsutter.wordpress.com/2010/03/13/...
dribeas -デビッド・ロドリゲスに

そして、必須のフォローアップ質問...これが他のコンパイラに組み込まれると予想されるのはいつですか:)
Jamie Cook

@ JohannesSchaub-litb同じ問題がありました。テンプレート関数のデフォルトのタイプを指定する可能性はありません。デフォルトのタイプの関数の明示的なインスタンス化によって解決しました(double私の場合)。多分それは「一般的」ではないかもしれませんが、この慣習には欠点がありますか?ありがとう。
JackOLantern 2013

次のコードはコンパイルに失敗し、のようなエラーが発生します。error: invalid conversion from ‘int’ to ‘int*’理由は次のとおりです: `#include <array> #include <algorithm> #include <functional> template <typename Iterator、typename Comp = std :: less <Iterator>> void my_sort( Iterator beg、Iterator end、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()); } `
ルークピーターソン

36

C ++テンプレートを引用するには:完全ガイド(207ページ):

テンプレートが最初にC ++言語に追加されたとき、明示的な関数テンプレート引数は有効な構成ではありませんでした。関数テンプレートの引数は、常に呼び出し式から推定できる必要がありました。その結果、デフォルトは常に推定値によってオーバーライドされるため、デフォルトの関数テンプレート引数を許可する説得力のある理由はないように思われました。


シンプルで簡潔:)
InQusitive 2014

17

これまでのところ、提供されている関数テンプレートのデフォルトテンプレートパラメータの例はすべて、オーバーロードで実行できます。

AraK:

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
おそらくそれらの1つだけです。C ++の標準化プロセスは部分的に遅く実行されるため、変更によって標準の他の場所で機会または困難が生じた場合に気付く時間があります。規格の草案を実装する人々が矛盾や曖昧さを発見したときに、問題が進んでいくのを願っています。以前は許可されていなかったものを許可する機会は、コードを書きたい人が、違法である必要がなくなったことに気付いて頼りにする...
Steve Jessop

2
もう1つ:template<typename T = void> int SomeFunction();。ここでテンプレートパラメータが使用されることはなく、実際には関数が呼び出されることもありません。それが参照される唯一の場所は、decltypeまたはsizeofです。名前は意図的に別の関数の名前と一致しますが、それがテンプレートであるという事実は、コンパイラーが無料の関数(存在する場合)を優先することを意味します。2つはSFINAEで使用され、関数定義がない場合のデフォルトの動作を提供します。
トム

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();

2番目のパラメーターを明示的に設定する必要はないことがわかります。多分それは誰かのために役立つでしょう。


これは間違いなく答えではありません。
パピー

@deadmg-理由を説明できますか?私たちはすべてのC ++テンプレートの達人というわけではありません。ありがとう。
Kev

これはかなり便利な回避策ですが、必要なすべてのケースをカバーするわけではありません。たとえば、これをコンストラクタにどのように適用しますか?
Tiberiu Savin

@TiberiuSavin-私があなたを正しく理解していれば、次のようにすることができます。}。そして、次のように使用します。worker <int> w2(&my_array);
alariq 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.