web-dev-qa-db-ja.com

C ++のテンプレート関数内で自動(不明)タイプのベクトルを初期化する

未知のタイプのベクトルを生成したいテンプレート関数があります。自動にしようとしましたが、コンパイラは許可されていないと言っています。

テンプレート関数は、後続のメイン関数内のテストプログラムで見られるように、イテレータまたはポインタを取得します。どうすれば問題を解決できますか?

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;
}
16
axcelenator

autostd::vectorは使用できません。代わりに std :: iterator_traits を使用できます:

std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
43
Edgar Rokjān

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

のように ジョナサンの答えで

32
Vasiliy Galkin

あなたは何かのようなものを探しているかもしれません

std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);

デモ

7
Igor Tandetnik

iterator_traits を使用して、ポインタ/ iteratorの型情報を抽出できます。 value_type はあなたが興味を持っている特定の特性なので、次のことができます。

const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);

Live Example

3
Jonathan Mee

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);
}
3

タイプが不明であるというのは真実ではありません。作成するベクトルのタイプは、同じ種類の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>...
1
Davide Spataro

私はあなたの質問が求めているように見えるのとは少し異なる方法でこれを解決します。

まず、範囲は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が読み取り可能になります。