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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

2607閲覧

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

luma

総合スコア183

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

0クリップ

投稿2018/08/05 23:58

編集2018/08/06 01:17

STLのvectorは

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

によると,

cpp

1explicit vector(size_type n, const T& value = T(), 2 const Allocator& a = Allocator()); // (3) + (4) C++03 3 4template <class InputIter> 5vector(InputIter first, InputIter last, 6 const Allocator& a = Allocator()); // (5) C++03

とあるのですが,

例えば

cpp

1#include<vector> 2using namespace std; 3 4int main() { 5 vector<int> a(2, 3); 6 vector<int> b(begin(a), end(a)); 7 return 0; 8}

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

cpp

1/** a.cpp **/ 2#include<vector> 3using namespace std; 4 5template<class T> 6class X { 7public: 8 size_t n; 9 X(size_t n, const T& t = T()): n(n) { 10 } 11 template<class InputIter> 12 X(InputIter fi, InputIter la): n(distance(fi, la)) { 13 } 14}; 15 16int main() { 17 vector<int> a(2, 3); 18 vector<int> b(begin(a), end(a)); 19 X<int> x(2, 3); // compile error ~~(この行ではなくdistanceでerror)~~ すみませんここも compile errorでした 20 X<int> y(begin(a), end(a)); // ok 21 return 0; 22}

とすると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 to ‘distance(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)と書いていた行もエラーでしたすみません…

yohhoy👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2018/08/06 01:07

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

2018/08/06 01:08

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

回答2

0

ベストアンサー

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が、イテレーターとして有効な型に限定させる必要があります。
私はその場合、だいたいこんな感じでやっています。

c++

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

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


追記

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

投稿2018/08/06 01:24

編集2018/08/06 01:32
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

luma

2018/08/06 02:24 編集

なるほど,確かにできました. 後者はそのままでは一致度が高くなるが, , class = typename iterator_traits<InputIter>::value_type を付け足すと一致度が低くなるわけですか. size_tをintに変えてもコンパイルが通りました,なるほど… 追加で気になったのですが, これはSFINAEと呼ばれるものですか? また,vectorの実装もこんなふうになっているんですか?(色々あると思いますが…(追記ありがとうございます! とりあえず解決したのでベストアンサーとさせていただきます! ありがとうございました!
catsforepaw

2018/08/06 02:21

> これはSFINAEと呼ばれるものですか? そうです。C++11でその仕組みが確立したような気がします(type_traitsの導入とか)。 > vectorの実装もこんなふうになっているんですか?(色々あると思いますが… そんな風になっています。VC++では`class = enable_if_t<_Is_iterator_v<_Iter>>`となっていました。深くは追っていませんが、名前からしてイテレーターかどうかの判定をしていると思われます。g++でも同様のことをしているはずです。
luma

2018/08/06 02:24

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

0

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 が入力イテレータの要件を満たさなければオーバーロード解決に参加しないように変更された。

投稿2018/08/06 05:13

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問