質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.08%

[C++] vectorのコンストラクタはなぜ曖昧ではないのですか

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,297

luma

score 186

STLのvectorは

https://cpprefjp.github.io/reference/vector/op_constructor.html

によると,

explicit vector(size_type n, const T& value = T(),
                const Allocator& a = Allocator());   // (3) + (4) C++03

template <class InputIter>
vector(InputIter first, InputIter last,
      const Allocator& a = Allocator());             // (5) C++03

とあるのですが,

例えば

#include<vector>
using namespace std;

int main() {
  vector<int> a(2, 3);
  vector<int> b(begin(a), end(a));
  return 0;
}

これはコンパイルできますが,似たような設計にしようとして,

/** a.cpp **/
#include<vector>
using namespace std;

template<class T>
class X {
public:
  size_t n;
  X(size_t n, const T& t = T()): n(n) {
  }
  template<class InputIter>
  X(InputIter fi, InputIter la): n(distance(fi, la)) {
  }
};

int main() {
  vector<int> a(2, 3);
  vector<int> b(begin(a), end(a));
  X<int> x(2, 3); // compile error ~~(この行ではなくdistanceでerror)~~ すみませんここも compile errorでした
  X<int> y(begin(a), end(a)); // ok
  return 0;
}

とするとdistanceのところでdistance(int&, int&)がないといわれます.(追記参照)

explicitをつけたりもしましたが変わりませんでした.

どうすればvectorのコンストラクタのような振る舞いになるでしょうか.

g++のversionは7.3.0でした.

よろしくお願いします.

追記:

上記をコンパイルした結果です.

$ g++ a.cpp
a.cpp: In instantiation of ‘X<T>::X(InputIter, InputIter) [with InputIter = int; T = int]’:
a.cpp:18:16:   required from here
a.cpp:11:44: error: no matching function for call todistance(int&, int&)’
   X(InputIter fi, InputIter la): n(distance(fi, la)) {
                                    ~~~~~~~~^~~~~~~~
In file included from /usr/include/c++/7/bits/stl_algobase.h:66:0,
                 from /usr/include/c++/7/vector:60,
                 from a.cpp:1:
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: note: candidate: template<class _InputIterator> typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator)
     distance(_InputIterator __first, _InputIterator __last)
     ^~~~~~~~
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: note:   template argument deduction/substitution failed:
/usr/include/c++/7/bits/stl_iterator_base_funcs.h: In substitution of ‘template<class _InputIterator> typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator) [with _InputIterator = int]’:
a.cpp:11:44:   required from ‘X<T>::X(InputIter, InputIter) [with InputIter = int; T = int]’
a.cpp:18:16:   required from here
/usr/include/c++/7/bits/stl_iterator_base_funcs.h:138:5: error: no type named ‘difference_type’ in ‘struct std::iterator_traits<int>’

(この行ではなくdistanceでerror)と書いていた行もエラーでしたすみません…

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • BeatStar

    2018/08/06 10:07

    エラーが出た...じゃなくてどのようなエラーが吐かれたのか明記してください。それによって随分意味が変わってきますから。

    キャンセル

  • luma

    2018/08/06 10:08

    わかりました,コンパイル方法と一緒に追記しておきます.

    キャンセル

回答 2

checkベストアンサー

+4

X(size_t n, const T& t = T()): n(n) {

こちらは第一引数をsize_tとしていますが、main関数の中ではint値を渡しているため完全には一致せず、暗黙の変換を必要としています。

一方、

template<class InputIter>
X(InputIter fi, InputIter la): n(distance(fi, la)) {

こちらはあらゆる型が一致します。

つまり、二つのint値を渡した場合、一致度としてはこちらの方が高いことになり、こちらが呼ばれてしまって意図しない結果になった、ということです。

これを回避するには、テンプレート引数InputIterが、イテレーターとして有効な型に限定させる必要があります。
私はその場合、だいたいこんな感じでやっています。

template<class InputIter, class = typename iterator_traits<InputIter>::value_type>
X(InputIter fi, InputIter la) : n(distance(fi, la))


iterator_traitsクラステンプレートはイテレーターの情報を取得するクラスですが、テンプレート引数にイテレーターとして無効な型を渡すと、中身が空になります。したがって、引数にint値を渡すと、iterator_traits<int>は中身が空っぽの構造体となり、iterator_traits<int>::value_typeは存在しないため不一致となり、サイズ指定の方が呼ばれるようになります。


追記

実際のvectorクラスも同様のことをやっていますが、その方法はライブラリーの実装によって違うと思います。興味がおありでしたらvectorヘッダーの中身を見て実際にどのように書かれているか確認してみると良いでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/06 10:40 編集

    なるほど,確かにできました.
    後者はそのままでは一致度が高くなるが,
    , class = typename iterator_traits<InputIter>::value_type
    を付け足すと一致度が低くなるわけですか.
    size_tをintに変えてもコンパイルが通りました,なるほど…

    追加で気になったのですが,
    これはSFINAEと呼ばれるものですか?
    また,vectorの実装もこんなふうになっているんですか?(色々あると思いますが…(追記ありがとうございます!

    とりあえず解決したのでベストアンサーとさせていただきます!
    ありがとうございました!

    キャンセル

  • 2018/08/06 11:21

    > これはSFINAEと呼ばれるものですか?

    そうです。C++11でその仕組みが確立したような気がします(type_traitsの導入とか)。

    > vectorの実装もこんなふうになっているんですか?(色々あると思いますが…

    そんな風になっています。VC++では`class = enable_if_t<_Is_iterator_v<_Iter>>`となっていました。深くは追っていませんが、名前からしてイテレーターかどうかの判定をしていると思われます。g++でも同様のことをしているはずです。

    キャンセル

  • 2018/08/06 11:24

    追って答えていただきありがとうございます!

    キャンセル

+2

https://cpprefjp.github.io/reference/vector/op_constructor.html

備考
イテレータ範囲コンストラクタ(5)
template <class InputIter> vector(InputIter first, InputIter last, const Allocator& a = Allocator()) は、C++03 までは InputIter が整数型の場合には vector(static_cast<typename vector::size_type>(first), static_cast<typename vector::value_type>(last), a) と同等とされていたが、C++11 では InputIter が入力イテレータの要件を満たさなければオーバーロード解決に参加しないように変更された。

C++11 では InputIter が入力イテレータの要件を満たさなければオーバーロード解決に参加しないように変更された。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.08%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る