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

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

ただいまの
回答率

88.92%

テンプレート引数であるvectorの型をテンプレートで取得したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 411

diode

score 84

質問内容

std::vectorをユニバーサル参照で受け取り
そのvectorの要素の型と同じ変数varを作り
その新たに作成したvectorにコピー/ムーブ
したあとそれらを使って
処理を行う関数を作りたいのですが
エラーが発生してしまいます。
どうすればvector内の要素の型を取得できますか。

どうかよろしくお願い致します。

#include <utility>
#include <vector>

template<class Vec,typename Type>
void func(Vec<Type>&& vec){
    Type var = 0;//  ←  Typeが推論できない
    auto vec2 = std::forward<Vec>(vec);
    //
    //    変数varやvec2を使った処理が続く(省略)
    //
}

int main(void)
{
    std::vector<int> left_ref = {1,2,3,4,5};
    func(left_ref);//左辺値

    func(std::vector<int>{{1,2,3,4,5}});//右辺値
    return 0;
}


上記コードのエラーコード

/***/main.cpp:5:11: error: ‘Vecis not a template
 void func(Vec<Type>&& vec){
           ^~~
/***/main.cpp: In function ‘int main()’:
/***/main.cpp:16:18: error: no matching function for call to ‘func(std::vector<int>&)func(left_ref);//左辺値
                  ^
/***/main.cpp:5:6: note: candidate: template<class Vec, class Type> void func(Vec&&)
 void func(Vec<Type>&& vec){
      ^~~~
/***/main.cpp:5:6: note:   template argument deduction/substitution failed:
/***/main.cpp:16:18: note:   couldn't deduce template parameter ‘Typefunc(left_ref);//左辺値
                  ^
/***/main.cpp:18:39: error: no matching function for call to ‘func(std::vector<int>)func(std::vector<int>{{1,2,3,4,5}});//右辺値
                                       ^
/***/main.cpp:5:6: note: candidate: template<class Vec, class Type> void func(Vec&&)
 void func(Vec<Type>&& vec){
      ^~~~
/***/main.cpp:5:6: note:   template argument deduction/substitution failed:
/***/main.cpp:18:39: note:   couldn't deduce template parameter ‘Typefunc(std::vector<int>{{1,2,3,4,5}});//右辺値


エラー内容は

Vecはテンプレートではない。
funcの使い方がおかしい。
テンプレート引数の控除/置換に失敗しました。
テンプレートパラメータ「Type」を推定できませんでした。

とのことですが
ひょっとかしたらc++ではテンプレート引数のテンプレート引数
を推論できないのかな?
といったかんじです。

試したこと

#include <utility>
#include <vector>
#include <memory>
#include <iostream>

template<typename Type>
void func(const std::vector<Type>& vec){
    Type var = 0;
    auto vec2 = vec;
    //
    //    変数varやvec2を使った処理が続く(省略)
    //

    std::cout << "コピー" << std::endl;
}

template<typename Type>
void func(std::vector<Type>&& vec){
    Type var = 0;
    auto vec2 = std::move(vec);
    //
    //    変数varやvec2を使った処理が続く(省略)
    //

    std::cout << "ムーブ" << std::endl;
}

int main(void)
{
    std::vector<int> left_ref = {1,2,3,4,5};
    func(left_ref);//左辺値

    func(std::vector<int>{{1,2,3,4,5}});//右辺値
    return 0;
}


上記のようにオーバーロードで2つの関数を作れば
コンパイルできますが
std::forwardとユニバーサル参照を使って1つの関数で
済ましてしまいたいです。

開発環境の備考

ツールの種類 ツールの名前 バージョン
コンパイラ clang++ 6.0.0
OS Linux Mint 18.3
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

どうすればvector内の要素の型を取得できますか。

std::vector<T>コンテナが格納する値の型Tは、メンバ型value_type経由で取得できます。
(C++標準ライブラリ提供のコンテナクラスでは、同様にvalue_typeを提供します)

#include <type_traits>

template<class Vec>
void func(Vec&& vec){
    using Type = typename std::remove_cv_t<std::remove_reference_t<Vec>>::value_type;
    // using Type = std::remove_cvref_t<Vec>::value_type; // C++20以降
    Type var = 0;

    //...
}

https://qiita.com/7of9/items/6a3314af4a4369d85631 にあるコメントも参考にどうぞ。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/09 02:59 編集

    yohhoy様
    度々御返事頂き誠にありがとうございます。
    返信に時間がかかってしまい申し訳ございません。

    >本当にやりたい処理を理解してはいないため、"適切でない"は少々言い過ぎだったかもしれません。
    質問文で私が書いたコードは色々省略しすぎて
    わかりづらくなってしまっていたと思うので
    さらに具体的なコードを書きました。
    https://wandbox.org/permlink/G43xHmFyQkU4vW0s
    の145行目から173行目までのように
    +演算子のオーバーロードを
    左辺値が渡された場合const参照と推論できたら
    より安全なコードにできるなと考えていた
    ということです。

    この場合+operator内で引数の非constメンバにアクセスしていなように見えるので
    const参照と推論できたらより安全になると思うのですが
    それを実現する方法が無い(できたら完全転送の考え方と矛盾している)
    ように感じるので安全性を重視するなら
    質問文内の項目[試したこと]
    のようにオーバロードを2つ作る方法を
    とるべきなのでしょうか。

    短く書きたかったのですが結構大きなコードになってしまいました
    すいません。

    あと本題のvectorの要素の型を取得したいという
    質問自体は解決できたので問題を解決済みとさせていただきます。

    お手数お掛けしてしまいますが
    どうかお返事お待ちしております。

    キャンセル

  • 2020/07/09 10:25 編集

    設計問題なので絶対的な正解はありませんが、一時オブジェクト生成の回避を主目的にした場合、自分ならこうするかなという設計だけ共有しておきます:

    - M::operator+=(const M&)オーバーロードを実装する(a += b; でaをin-place更新)
    - M::operator+(const M& l, M&& r)とM::operator+(M&& r, const M& l)では return std::move(r) += l;
    - M::operator+(const M& a, const M& b) では return M{a} += b;

    エッジケースを考えるとさらにoperator+オーバーロードが必要かもしれません。複雑なテンプレートメタプログラミングよりは素直にオーバーロード追加していくほうが簡潔になるだろうという方針です。

    キャンセル

  • 2020/07/09 13:39

    yohhoy様
    度々御返事頂き誠にありがとうございます。

    確かにenable_ifの周りが
    複雑になりすぎてすぎて私自身うっすら
    これ読み手にとって嫌がられる
    コードではないかな…と
    心配していましたが

    アドバイス頂けとても助かりました。
    この経験を私のコーディングスタイルに
    反映していこうと考えております。

    数日間お付き合いいただけ
    とても光栄に感じております。

    大変ありがとうございました。

    キャンセル

0

テンプレートを受け取る場合にはそのための記法があります。 テンプレートテンプレートと呼ばれているのでこれをキーワードにして検索してみるといいでしょう。

たとえばこのようになります。

#include <utility>
#include <vector>

template<typename Type, typename Al, template<class,class>class Vec>
void func(Vec<Type,Al>&& vec){
    Type var = 0;//  ←  Typeが推論できない
    auto vec2 = std::move(vec);
    //    変数varやvec2を使った処理が続く(省略)
}

int main(void)
{
    // ユニバーサル参照ではないので左辺値参照に非対応
    // std::vector<int> left_ref = {1,2,3,4,5};
    // func(left_ref);//左辺値

    func(std::vector<int>{{1,2,3,4,5}});//右辺値
    return 0;
}

ただし、このときの && はユニバーサル参照にはならないので左辺値の場合と右辺値の場合を個別に書く必要があり、普通は避けた方が好ましいです。

なので、一般的には yohhoy 氏が提案しているような方法が使われます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/06 16:45

    テンプレート・テンプレートによる解法は、std::vectorは2個のテンプレートパラメータをとるという事前知識が必要になるので、おすすめし辛いことが多いですね :P

    FYI: C++17だったか、どこかのタイミングで template<typename Type, template<class>class Vec>/Vec<Type> が許容されるようになっています。
    https://wandbox.org/permlink/QRMttzMtCML1f5EX

    キャンセル

  • 2020/07/06 22:55 編集

    SaitoAtsushi様
    返信に時間がかかってしまい申し訳ございません。
    貴重なご意見ありがとうございます。

    テンプレートテンプレートという言葉は初耳でしたので
    難しそうですがちょっと調べてみます。

    ご回答誠にありがとうございました。

    キャンセル

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

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

関連した質問

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