web-dev-qa-db-ja.com

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

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

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

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

182
Arman

デフォルトのテンプレート引数を指定することは理にかなっています。たとえば、ソート関数を作成できます。

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によるこの欠陥レポートを参照してください。 関数テンプレートのデフォルトのテンプレート引数 および彼の言うこと

関数テンプレートのデフォルトのテンプレート引数の禁止は、独立した関数がセカンドクラスの市民として扱われ、すべてのテンプレート引数を指定ではなく関数引数から推定する必要があった時代の名残misです。

この制限により、メンバー関数とは異なる独立した関数が不必要に作成され、STLスタイルのコードを記述しにくくなるため、プログラミングスタイルが大幅に制約されます。

145

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

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

35
James McNellis

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

アラク:

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の欠陥レポートが示すように、非推定パラメーターの追加は遅すぎて、デフォルトが有用になったことを誰もが理解および/または実際に認識することができませんでした。したがって、現在の状況は、標準ではない機能テンプレートのバージョンに基づいて有効になっています。

17
Steve Jessop

Windowsでは、Visual Studioのすべてのバージョンで、このエラー( C4519 )を警告に変換したり、次のように無効にしたりできます。

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

詳細を参照してください こちら

4
Adi Shavit

私が使用するのは次のトリックです:

このような機能を持ちたいとしましょう:

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番目のパラメーターを明示的に設定する必要はありません。たぶんそれは誰かに役立つでしょう。

1
alariq