戻り値の型に基づいて関数のテンプレートを推定しますか?
-
25-09-2019 - |
質問
テンプレート推論を使用して次のことを達成できるようにしたいと考えています。
GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();
(私が現在持っているもの) の代わりに:
GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();
現在の Allocate 関数は次のようになります。
class GC
{
public:
template <typename T>
static GCPtr<T> Allocate();
};
これで余分なものを取り除くことは可能でしょうか <A>
そして <B>
?
解決
これは実行できません。戻り値の型は、型推論に関与しない、それはむしろ、すでに適切なテンプレート署名を一致した結果です。
:あなたは、それにもかかわらず、として、ほとんどの用途からそれを隠すことができます// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
p = GC::Allocate<T>();
}
int main()
{
GCPtr<A> p = 0;
Allocate(p);
}
構文がどの良くも悪くも初期GCPtr<A> p = GC::Allocate<A>()
よりも実際にあることかどうかは別問題です。
P.S。 C ++ 11を使用すると、型宣言の一つをスキップできるようになります:
auto p = GC::Allocate<A>(); // p is of type GCPtr<A>
他のヒント
私は考えることができる唯一のこと。
:makeが実際の作業を行い、テンプレート変換演算子を持つ非テンプレートそのリターンは非テンプレートプロキシオブジェクトを割り当てますtemplate <class T>
struct GCPtr
{
};
class Allocator
{
public:
template <class T>
operator GCPtr<T>() { return GCPtr<T>(); }
};
class GC
{
public:
static Allocator Allocate() { return Allocator(); }//could give a call-back pointer?
};
int main()
{
GCPtr<int> p = GC::Allocate();
}
あなたは反対のルートを行くことができます。
あなたは(数日、またはGCCの現在のバージョンで実行する必要がありますMSVC 2010)およびC ++ 0xのに頼る気にしない日付コンパイラまでを使用する場合は、特徴:
auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();
だけではない右側に、あなたに余分な<A>
と<B>
を保存します。 :)
(この答えは @UncleBens と同じですが、引数を完全転送するため、もう少し一般的です。)
これは、haskell のような言語で非常に便利です。たとえば、 read
文字列を入力として受け取り、必要な戻り値の型に従ってそれを解析します。
(ここは ideoneのサンプルコード.)
まずは関数から foo
その戻り値の型を推測したいのは次のとおりです。
template<typename Ret>
Ret foo(const char *,int);
template<>
std::string foo<std::string>(const char *s,int) { return s; }
template<>
int foo<int >(const char *,int i) { return i; }
文字列を要求されると、最初の引数にある文字列が返されます。int を要求されると、2 番目の引数が返されます。
関数を定義できます auto_foo
これは次のように使用できます。
int main() {
std::string s = auto_foo("hi",5); std::cout << s << std::endl;
int i = auto_foo("hi",5); std::cout << i << std::endl;
}
これを機能させるには、関数の引数を一時的に保存し、要求されたときに関数を実行するオブジェクトが必要です。 変換する 目的の戻り値の型に変更します。
#include<tuple>
template<size_t num_args, typename ...T>
class Foo;
template<typename ...T>
class Foo<2,T...> : public std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); }
};
template<typename ...T>
class Foo<3,T...> : std::tuple<T&&...>
{
public:
Foo(T&&... args) :
std::tuple<T&&...>(std::forward<T>(args)...)
{}
template< typename Return >
operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); }
};
template<typename ...T>
auto
auto_foo(T&&... args)
// -> Foo<T&&...> // old, incorrect, code
-> Foo< sizeof...(T), T&&...> // to count the arguments
{
return {std::forward<T>(args)...};
}
また、上記は 2 引数または 3 引数の関数でも機能します。それを拡張する方法を理解するのは難しくありません。
書かなければならないコードはたくさんあります。これを適用する関数ごとに、これを実行するマクロを作成できます。ファイルの先頭には次のような内容があります。
REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
// necessary structure and auto_???
そして、あなたは使うことができます auto_foo
あなたのプログラムで。
は、戻り値の型で関数をオーバーロードすることはできません同じように、あなたはそれにテンプレート控除を行うことはできません。そして、同じ理由 - fは()を返すの何かが、どのようなタイプここで使用することをテンプレート/過負荷がある場合:
f();
あなたはそれのためにマクロを使用しようとすることができます。それ以外は、私はそれがちょうど1文で動作するようになっていますどのように表示されません。
#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()
ALLOC(ptr1,A);
ヨハネスポイントが有効です。 >>問題は簡単に固定されています。タイプの一部が延長C99を必要とプリプロセッサ可変引数としてしかし、私は持っコンマ思います:
#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()
ALLOC(ptr1,SomeTemplate<int,short>);