🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

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

Q&A

解決済

2回答

2076閲覧

自作vector。コンストラクタでコンパイルエラーが出て直せない

yoshiki_iwasa

総合スコア23

C++

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

0グッド

1クリップ

投稿2021/01/14 09:37

# 質問概要

現在、std::vector を以下の Referenceを使って、C++98で作っているのですが、コンストラクタのオーバーロードがうまくいかず、コンパイルエラーが出ます。解決方法がわかりません (T T)/

参考にしているドキュメント

質問詳細

以下のようなコードをコンパイルすると、エラーが出ます。(自作 vector は ft 名前空間に作っています。)

C++

1int main() 2{ 3 ft::vector<int> a(10, 42); <- fill constructor を呼び出したい。 4}

エラー文

Shell

1error: indirection requires pointer operand ('int' invalid) 2 this->_pointer[i] = *first; 3 ^~~~~~ 4 in instantiation of function template specialization 'ft::vector<int>::vector<int>' requested here 5 ft::vector<int> a(10, 42); 6 7(⇡ range コンストラクタが呼び出されている)

エラーが出る原因は、以下の2つのコンストラクタです。

C++

1/* fill constructor */ 2 3template<class T> 4 vector<T>::vector(size_type n, const value_type& val, const allocator_type& alloc) 5 { 6 this->_pointer = alloc.allocate(n); 7 this->_size = n; 8 this->_capacity = n; 9 10 for(size_type i = 0; i < this->_size; i++) 11 { 12 this->_pointer[i] = val; 13 } 14 } 15 16

C++

1 2/* range constructor */ 3 4 template<class T> 5 template<class InputIterator> 6 vector<T>::vector(InputIterator first, InputIterator last,const allocator_type& alloc) 7 { 8 this->_size = last - first; 9 this->_capacity = last - first; 10 this->_pointer = alloc.allocate(this->_capacity); 11 12 for(size_type i = 0; i < this->_size; i++) 13 { 14 this->_pointer[i] = *first; 15 first++; 16 } 17 } 18 19

つまり、 fill コンストラクタを起動させて、コンテナa を 10個の42で初期化したいのですが、range コンストラクタがコンパイルエラーになってしまうのです。

確かに、テンプレート引数を考えると、range コンストラクタ も呼び出せるのでエラーが出るのは最もだとは思うのですが、それだと困るのです〜

自分の所望の動作は、以下のようになっていた時、a,b が整数なら fill Constructor を呼び出し、a,b がポインタ又はイテレータなら range constructor が呼び出されるように制御したいのですが、その方法はありませんか??

int main() { ft::vector<int> vec(a, b); }

また、ライブラリのvectorはどうやってこの問題を解決しているのでしょうか??

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

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

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

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

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

guest

回答2

0

ベストアンサー

std::enabled_if<> を使って、InputIterator が本当にイテレータであるかどうかチェックすればよいです。

c++

1 vector(std::enable_if<InputIteratorがイテレータなら, 2 InpuIterator>::type first, 3 std::enable_if<InputIteratorがイテレータなら, 4 InpuIterator>::type last, 5 const allocator_type& alloc);

libc++を見ると、イテレータチェックは std::is_convertible<typename iterator_traits<InputIterator>::iterator_category, std::input_iterator_tag>::value などでやっているようです。

追記: 次の質問と回答で言及しているとおり、2つあるInputIterator引数の両方をenabled_ifにするのは問題があります。

投稿2021/01/14 14:07

編集2021/01/16 13:20
int32_t

総合スコア21679

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

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

yoshiki_iwasa

2021/01/15 01:26

ありがとうございます! これと同じことを C++98までの機能で実現するとすると、どうするのがいいのでしょうか??
int32_t

2021/01/15 01:35

std::enable_if<>相当のものを自分で実装すればよいかと。
yoshiki_iwasa

2021/01/15 01:38

SFINAE という機能はC++11 からのものかと思っていたので、std::enable_if<> 的なものを作っても無駄かと思っていたのですが、認識が違ってますか?
int32_t

2021/01/15 01:50

SFINAE自体はC++11より前からある挙動です。
yoshiki_iwasa

2021/01/15 01:53

あ、そうなんですか!  バージョンが上がるにつれて改良が加えられてるだけでもともと言語に備わっている機能という認識であってますか??
int32_t

2021/01/15 01:59

その認識でよいと思います。 ところで、std::vector と完全に同じインタフェイスにする必要がないなら、私なら vector::create_with_range(InputIterator, InputIterator, const allocator_type&) とかして問題を回避したいですね。
yoshiki_iwasa

2021/01/15 02:04

これ実は学校の課題なんですが、インターフェースは合わせないといけないんです(T T) しかも、本当は存在しないパブリックメンバ関数を加えることが禁止されてます笑 なので、std::enable_if<>とstd::conditional<> を自分で作ります
int32_t

2021/01/15 02:18

学校の課題にしては過負荷な気がしますね。学校の環境が本当にC++98しか使えないのか確認するといいかもしれません。
yoshiki_iwasa

2021/01/15 02:38

C++98だけだよ〜ってサブジェクトに明記してあるので間違いはないですね笑 うちの学校そういうところあって、C言語だけでレイトレーシングさせて3Dグラフィック作らせたり、アセンブラでatoi() 作らせたりそういう面倒なことを色々要求してくるんです なので、今回もおそらく、enable_if とか便利な道具を自分で作ってねってことなんだと思います
yumetodo

2021/01/16 06:12

ぜひ課題を出した人に古いC++を使うことは20%からの速度面での不利があるというC++の教祖のお言葉を伝えたいところですね。
yumetodo

2021/01/16 06:14

というか今どきenum hack使わせるとかどういう神経してるんだ。旧石器時代の技法なんか使わせて何になるというのだろう。(std::enable_ifやらを自作するのに必要な技法)
guest

0

SFINAEでイテレーターであることを確認していると思います。

イメージとしてはこんなのを作ります。

cpp

1namespace inferior{ 2 template<bool b> 3 using enable_if_t = typename std::enable_if<b, std::nullptr_t>::type; 4 template<bool b> 5 using bool_constant = std::integral_constant<bool, b>; 6 template<typename...> 7 using void_t = void; 8 template<typename T, typename = void> 9 struct is_iterator : std::false_type {}; 10 template<typename T> 11 struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>> 12 : std::true_type 13 {}; 14 template<typename Iterator, bool is_iterator = is_iterator<Iterator>::value> 15 struct is_input_iterator : std::false_type {}; 16 template<typename Iterator> 17 struct is_input_iterator<Iterator, true> : std::is_base_of< 18 std::input_iterator_tag, 19 typename std::iterator_traits<Iterator>::iterator_category 20 > {}; 21 template<typename InputIter> 22 using require_input_iterator = enable_if_t< 23 inferior::is_input_iterator<InputIter>::value 24 >; 25}

実際のSTLの実装を見てみてもそうなっていますね。

msvc: https://github.com/microsoft/STL/blob/ac4fde764ae716512bdd67e0e9577bd53947bc3d/stl/inc/vector#L507-L508
libstdc++: https://github.com/gcc-mirror/gcc/blob/d61d2a5f3ce9238bad7cbd7733d90c6ec7ae5fbc/libstdc%2B%2B-v3/include/bits/stl_vector.h#L651-L654

投稿2021/01/14 14:04

編集2021/01/15 01:16
yumetodo

総合スコア5852

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

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

yoshiki_iwasa

2021/01/15 01:24

ありがとうございます! std::enable_if と SFINAE は C++11 からの導入のようですが、C++98 の時代にはこれをどうやって解決していたのでしょうか??
yumetodo

2021/01/16 06:16

void_tは再現できんやろうから複数段階にわたってラッパーtraits classを書くことになるんだろうな、絶対にやりたくない。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問