未知のタイプのベクトルを生成したいテンプレート関数があります。自動にしようとしましたが、コンパイラは許可されていないと言っています。
テンプレート関数は、後続のメイン関数内のテストプログラムで見られるように、イテレータまたはポインタを取得します。どうすれば問題を解決できますか?
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
auto size = distance(beg, end);
vector<auto> temp(size); // <--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(beg, end, temp->begin);
.
.
return ....
}
int main()
{
int bips[] = {3, 7, 0, 60, 17}; // Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
vector<int> v = {10, 5, 4, 14}; // Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
auto
のstd::vector
は使用できません。代わりに std :: iterator_traits を使用できます:
std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
C++ 17互換のコンパイラを使用している場合は、 クラステンプレート引数の控除 から利益を得ることができます。
したがって、ベクトルをstd::copy
で埋める特別な理由がない限り、次のようにコードを記述できます。
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
vector temp(beg, end);
// do the remaining stuff
return ....
}
この機能がコンパイラで利用できない場合、私は投票します
vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
のように ジョナサンの答えで
あなたは何かのようなものを探しているかもしれません
std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);
iterator_traits
を使用して、ポインタ/ iterator
の型情報を抽出できます。 value_type
はあなたが興味を持っている特定の特性なので、次のことができます。
const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
auto
が機能しないのは、そのコンテキストでは許可されていないためです。テンプレート引数の代わりにauto
を指定することはできません。コンパイラーにテンプレート引数を自動的に推測させたい場合の正しい行動方針は、引数をまったく提供しないことです。ただし、この場合、コンパイラーがその型がどうあるべきかを推測する方法はありません。タイプを明示的に指定する必要があります。
あなたのベクトルの正しいタイプを見つける方法はたくさんあります。 std::iterator_traits
を使用して、イテレータが参照する値のタイプなど、イテレータに関する情報を取得できます。 typename std::iterator_traits<Iter>::value_type
を使用します。
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <vector>
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw std::domain_error("empty vector");
auto size = std::distance(beg, end);
using t_value = typename std::iterator_traits<Iter>::value_type;
std::vector<t_value> temp(size);
std::copy(beg, end, temp.begin());
return temp;
}
int main()
{
int bips[] = { 3,7,0,60,17 };//Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
std::vector<int> v = { 10,5,4,14 };//Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
0サイズの範囲をチェックする理由はないことを指摘したいと思います。空のベクトルを正しく返します。
my_func
にはイテレータのペアを受け入れ、その範囲をコピーするコンストラクタがあるという事実を利用して、std::vector
の本体をかなり単純化することもできます。
template<class Iter>
auto my_func(Iter beg, Iter end)
{
using t_value =typename std::iterator_traits<Iter>::value_type;
return std::vector<t_value>(beg, end);
}
タイプが不明であるというのは真実ではありません。作成するベクトルのタイプは、同じ種類のIter
です。
次のように、iter
を使用するか、iterator type traitを使用して、decltype
の基になる型を取得します。
decltype
-> std::vector<typename remove_reference<decltype(*beg)>::type> temp(beg, end);
iterator type trait
次のように
using Type = std::iterator_traits<Iter>::value_type;
std::vector<Type>...
私はあなたの質問が求めているように見えるのとは少し異なる方法でこれを解決します。
まず、範囲は2つのイテレータを使用するよりも基本的なタイプとして優れていることがわかりました。 2つのイテレータは結合されており、1つの引数である必要があります。範囲は、2つのイテレータの単純な構造体であり、いくつかのユーティリティメソッドがあります。
_template<class It>
struct range_t:
std::iterator_traits<It>
{
It b{}, e{};
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
auto size() const { return std::distance(begin(), end()); }
// etc
range_t()=default;
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
};
template<class It>
range_t<It> make_range( It s, It f ) { return {std::move(s), std::move(f)}; }
_
_range_t
_ sは、開始終了イテレータを正しく結合します。
今
_template<class Range>
auto my_func(Range&& range) {
// todo
}
template<class Iter>
auto my_func(Iter beg, Iter end)
{
return my_func(make_range(std::move(beg), std::move(end)));
}
_
最初のステップです。または、2イテレータバージョンを完全に削除し、呼び出し元がイテレータをパッケージ化することを期待します。
_template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
// todo
}
_
さて、これを実行します。
_auto size = range.size();
vector<auto> temp(size);//<--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(range.begin(), range.end(), temp->begin);
_
しかし、それは一般的な操作です。だから私たちは範囲のためにそれを書きます:
_template<class Range>
auto as_vector( Range const& r ) {
using value_type = typename Range::value_type;
std::vector<value_type> v( range.begin(), range.end() );
return v;
}
_
私たちに与える:
_template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
auto v = as_vector(range);
// ...
return ...;
}
_
問題を意味のある単純なプリミティブに分解し、実装の複雑さをそれらのプリミティブに移しました。 _my_func
_の「ビジネスロジック」は、範囲をベクトルにするために実行する手順を気にしなくなりました。
これにより、as_vector(range)
が実際にその範囲をベクトルとして返すという信頼がある限り、_my_func
_ moreが読み取り可能になります。