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

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

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

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

Q&A

解決済

2回答

2173閲覧

conceptを使った関数でコンパイルエラーが発生する

退会済みユーザー

退会済みユーザー

総合スコア0

C++

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

0グッド

0クリップ

投稿2020/08/04 20:10

編集2020/08/05 08:30

質問内容

私はSFINAEとconceptを使い、
スマートポインタのテンプレート引数Typeに依存せずに
入力された下記オブジェクトを判別できるはずの関数
identificationを作成しました。

  • std::unique_ptr<Type>
  • std::shared_ptr<Type>
  • std::weak_ptr<Type>
  • ポインタ(動的配列)
  • 静的配列
  • その他すべて

しかしテンプレート引数Typeをint[]とするとエラーが発生してしまいます。
エラー文を読んでも何が言いたいのかよくわからないです。
どのように書けばエラーを抑えることができるでしょうか。

あとconceptは勉強し始めたばかりで慣れていませんので
あまり良い書き方でない部分があれば
教えていただければ幸いです。

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

元コード

どんどん追記していくうちに文字数制限に引っかかってしまったのでので元コードを
wandboxへ移動しました。

エラー文

terminal

1[ 50%] Building CXX object CMakeFiles/a.out.dir/main.cpp.o 2[ソースのパス]/main.cpp:112:20: error: call to deleted constructor of 'std::unique_ptr<int [], std::default_delete<int []> >' 3 identification(heap_array); 4 ^~~~~~~~~~ 5 6/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:689:7: note: 'unique_ptr' has been explicitly marked deleted here 7 unique_ptr(const unique_ptr&) = delete; 8 ^ 9 10[ソースのパス]/main.cpp:76:28: note: passing argument to parameter here 11auto identification(auto...)noexcept{ // その他が入力された場合実行 12 ^

####エラー内容

  • error:'std::unique_ptr<int[]、std::default_delete<int[]>>'の削除されたコンストラクターの呼び出し

何が言いたいのか私にはわかりません。

  • note:「unique_ptr」はここで明示的に削除済みとしてマークされています

これも何が言いたいのか私にはわかりません。

  • note: ここに引数をパラメータに渡します

この部分はコード中のエラーの発生する部分をコメントアウト
すれば消えるので恐らく今回の問題とは直接関係ないかもしれないです。

開発環境の備考

上記コードはすべてc++20でコンパイルするものとする。

ツールの種類ツールの名前バージョン
コンパイラclang++10.0.0
OSLinux Mint20.0

追記

Bearded-Ockhamさんの回答を元に修正した
問題を解決するためのコードを下記に記す。

c++

1#include <iostream> 2#include <memory> 3#include <vector> 4 5/////////////////// 6// 7// コンセプト定義 8// 9/////////////////// 10template<typename Type> concept IsUniquePtr = requires(Type type1, Type& type2){ // std::unique_ptrか判別 11 type1 = nullptr; 12 type1.release(); 13 type1.reset(); 14 type1.swap(type2); 15 type1.get(); 16 type1.get_deleter(); 17 //*type1; <- ここがエラーの原因 18 type1.operator bool(); 19}; 20 21template<typename Type> concept IsSharedPtr = requires(Type type1, Type& type2){ // std::shared_ptrか判別 22 type1 = type2; 23 type1.reset(); 24 type1.swap(type2); 25 type1.get(); 26 //*type1; <- はstd::shared_ptr<int[]>が入力されたときetcと表示してしまう原因となる。 27 type1.use_count(); 28 type1.unique(); 29 type1.operator bool(); 30 type1.owner_before(type2); 31}; 32 33template<typename Type> concept IsWeakPtr = requires(Type type1, Type& type2){ // std::weak_ptrか判別 34 type1 = type2; 35 type1.swap(type2); 36 type1.reset(); 37 type1.use_count(); 38 type1.expired(); 39 type1.lock(); 40 type1.owner_before(type2); 41}; 42 43template<typename Type> concept IsPointer = std::is_pointer_v<Type>; // ポインタ化判別 44template<typename Type> concept IsArray = std::is_array_v<Type>; // 配列か判別 45 46//////////////////////////////////// 47// 48// スマートポインタや配列の識別関数 49// 50//////////////////////////////////// 51template<IsUniquePtr Up> 52auto identification(const Up&)noexcept{ // unique_ptrが入力された場合実行 53 std::cout << "unique_ptr" << std::endl; 54} 55 56template<IsSharedPtr Sp> 57auto identification(const Sp&)noexcept{ // shared_ptrが入力された場合実行 58 std::cout << "shared_ptr" << std::endl; 59} 60 61template<IsWeakPtr Wp> 62auto identification(const Wp&)noexcept{ // weak_ptrが入力された場合実行 63 std::cout << "weak_ptr" << std::endl; 64} 65 66template<IsPointer Pt> 67auto identification(const Pt&)noexcept{ // ポインタが入力された場合実行 68 std::cout << "pointer" << std::endl; 69} 70 71template<IsArray Ar> 72auto identification(const Ar&)noexcept{ // 配列が入力された場合実行 73 std::cout << "array" << std::endl; 74} 75 76auto identification(auto...)noexcept{ // その他が入力された場合実行 77 std::cout << "etc" << std::endl; 78} 79 80////////////// 81// 82// メイン関数 83// 84////////////// 85int main(){ 86 // スマートポインタの型には依存しない 87 using UseType = float; 88 //using Type = int; 89 //using Type = std::vector<int>; 90 91 // スマートポインタや配列や動的配列確保 92 auto unique_ptr_mem = std::make_unique<UseType>(); 93 auto shared_ptr_mem = std::make_shared<UseType>(); 94 auto weak_ptr_mem = std::weak_ptr<UseType>(); 95 auto vector_mem = std::vector<UseType>(); 96 auto pointer_mem = new UseType[3]; 97 UseType array_mem[3]; 98 99 identification(unique_ptr_mem);// unique_ptrと表示される(正常) 100 identification(shared_ptr_mem);// shared_ptrと表示される(正常) 101 identification(weak_ptr_mem);// weak_ptrと表示される(正常) 102 identification(pointer_mem);// pointerと表示される(正常) 103 identification(array_mem);// arrayと表示される(正常) 104 identification(vector_mem);// etcと表示される(正常) 105 106 delete[] pointer_mem; 107 108 // 109 // ここから問題の部分 110 // 111 auto heap_array = std::make_unique<int[]>(5); 112 identification(heap_array);// unique_ptrと表示される!!!(正常) 113 114 return 0; 115} 116

追記

yohhoyさんの回答を元に修正した
問題を解決するためのコードを下記に記す。

c++

1#include <iostream> 2#include <memory> 3#include <vector> 4 5//////////////////////////////////////////////////////////////////////////////////////////// 6// 7// コンセプト定義 8// 9// 備考: 10// typename スマートポインタ::element_typeでスマートポインタのテンプレート引数を参照可能 11// 12// また、スマートポインタの場合テンプレート引数が配列か否かで作られるクラス形が異なるので 13// コンセプトはそれらのコンセプトの論理和でなければならない。 14// 15//////////////////////////////////////////////////////////////////////////////////////////// 16 17template<typename Type> concept IsUniquePtr = // std::unique_ptrか判別 18 std::is_same_v<Type, std::unique_ptr<typename Type::element_type>> // テンプレート引数が配列型以外の場合 19 || std::is_same_v<Type, std::unique_ptr<typename Type::element_type[]>>; // テンプレート引数が配列型の場合 20 21template<typename Type> concept IsSharedPtr = // std::shared_ptrか判別 22 std::is_same_v<Type, std::shared_ptr<typename Type::element_type>> // テンプレート引数が配列型以外の場合 23 || std::is_same_v<Type, std::shared_ptr<typename Type::element_type[]>>; // テンプレート引数が配列型の場合 24 25template<typename Type> concept IsWeakPtr = // std::weak_ptrか判別 26 std::is_same_v<Type, std::weak_ptr<typename Type::element_type>> // テンプレート引数が配列型以外の場合 27 || std::is_same_v<Type, std::weak_ptr<typename Type::element_type[]>>; // テンプレート引数が配列型の場合 28 29 30template<typename Type> concept IsPointer = std::is_pointer_v<Type>; // ポインタか判別 31template<typename Type> concept IsArray = std::is_array_v<Type>; // 配列か判別 32 33//////////////////////////////////// 34// 35// スマートポインタや配列の識別関数 36// 37//////////////////////////////////// 38inline auto identification(IsUniquePtr auto const &){ // unique_ptrが入力された場合実行 39 std::cout << "unique_ptr" << std::endl; 40} 41 42inline auto identification(IsSharedPtr auto const &){ // shared_ptrが入力された場合実行 43 std::cout << "shared_ptr" << std::endl; 44} 45 46inline auto identification(IsWeakPtr auto const &){ // weak_ptrが入力された場合実行 47 std::cout << "weak_ptr" << std::endl; 48} 49 50inline auto identification(IsPointer auto const &){ // ポインタが入力された場合実行 51 std::cout << "pointer" << std::endl; 52} 53 54inline auto identification(IsArray auto const &){ // 配列が入力された場合実行 55 std::cout << "array" << std::endl; 56} 57 58inline auto identification(auto...){ // その他が入力された場合実行 59 std::cout << "etc" << std::endl; 60} 61 62////////////// 63// 64// メイン関数 65// 66////////////// 67int main(){ 68 // スマートポインタの型には依存しない 69 using UseType = float; 70 //using Type = int; 71 //using Type = std::vector<int>; 72 73 //配列などの定数 74 constexpr size_t size = 5; 75 76 // スマートポインタや配列や動的配列確保 77 auto unique_ptr_mem = std::make_unique<UseType>(); 78 auto shared_ptr_mem = std::make_shared<UseType>(); 79 auto weak_ptr_mem = std::weak_ptr<UseType>(); 80 auto vector_mem = std::vector<UseType>(); 81 auto pointer_mem = new UseType[size]; 82 UseType array_mem[size]; 83 84 identification(unique_ptr_mem); // unique_ptrと表示される(正常) 85 identification(shared_ptr_mem); // shared_ptrと表示される(正常) 86 identification(weak_ptr_mem); // weak_ptrと表示される(正常) 87 identification(pointer_mem); // pointerと表示される(正常) 88 identification(array_mem); // arrayと表示される(正常) 89 identification(vector_mem); // etcと表示される(正常) 90 91 delete[] pointer_mem; 92 93 // 94 // ここから問題の部分 95 // 96 std::cout << std::endl << "-----ここからスマートポインタを使った動的配列の部分-----" << std::endl; 97 98 auto heap_array_unique = std::make_unique<int[]>(size); 99 auto heap_array_shared = std::make_shared<int[]>(size); 100 auto heap_array_weak = std::weak_ptr<int[]>(heap_array_shared); 101 102 identification(heap_array_unique); // unique_ptrと表示される!!!!!(正常) 103 identification(heap_array_shared); // shared_ptrと表示される!!!!!(正常) 104 identification(heap_array_weak); // weak_ptrと表示される!!!!!(正常) 105 106 return 0; 107} 108

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

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

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

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

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

guest

回答2

0

ベストアンサー

配列をパラメータに取るunique_ptrには、operator*が定義されていません(その代わりoperator[]があります)。このため、std::unique_ptr<int[]>には*type1;がないので、その他のテンプレートが使われています。その他のテンプレートの引数は、参照ではないので引数をコピーコンストラクタを使ってコピーしようとしますが、unique_ptrにはコピーコンストラクタがないのでエラーになっています。

投稿2020/08/04 21:59

Bearded-Ockham

総合スコア430

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

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

退会済みユーザー

退会済みユーザー

2020/08/04 23:19 編集

Bearded-Ockhamさん 御回答誠に有難うございます。 > 配列をパラメータに取るunique_ptrには、operator*が定義されていません(その代わりoperator[]があります)。このため、std::unique_ptr<int[]>には*type1;がない この部分は知りませんでした... とても勉強になります。 上記を踏まえれば identification(heap_array);は auto identification(auto...)noexcept{ // その他が入力された場合実行 std::cout << "etc" << std::endl; } に推論される。 しかし引数は参照渡しではないのでコピーが作成されるはずだが std::unique_ptrはコピーできないのでエラー発生。 ということですね。 requires内の*type1;を消したら 問題なく動くようになりましたので修正後のコードを 質問文に追記しておきます。 的確なご回答誠に有難うございました。
guest

0

別解の一つです。質問本文にあるコンセプト定義では「std::XXX_ptrと同じように振る舞えるスマートポインタ型か否か」を判定しますが、発想を変えて直接std::XXX_ptr型との同一判定を行うコンセプトも記述できますね。

C++

1template<typename Type> concept IsUniquePtr = 2 std::is_same_v<Type, std::unique_ptr<typename Type::element_type, typename Type::deleter_type>> 3 || std::is_same_v<Type, std::unique_ptr<typename Type::element_type[], typename Type::deleter_type>>; 4 5template<typename Type> concept IsSharedPtr = 6 std::is_same_v<Type, std::shared_ptr<typename Type::element_type>>; 7 8template<typename Type> concept IsWeakPtr = 9 std::is_same_v<Type, std::weak_ptr<typename Type::element_type>>;

おまけ:本題からは外れますが、各関数の記述を短縮表記することもできます。(noexceptは適切でないためついでに削除)

C++

1template<IsArray Ar> 2auto identification(const Ar&)noexcept{ 3 std::cout << "array" << std::endl; 4} 5// ↓ 6auto identification(IsArray auto const &) { 7 std::cout << "array" << std::endl; 8}

投稿2020/08/05 05:11

編集2020/08/05 05:19
yohhoy

総合スコア6191

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

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

退会済みユーザー

退会済みユーザー

2020/08/05 08:48 編集

yohhoyさん 御回答誠に有難うございます。 私はstd::vectorのvalue_typeのように スマートポインタでもelement_typeやdeleter_typeで 型情報を取得できると云うことは知りませんでしたので とても勉強になりました。 ありがとうございます。 また別解のコードでは std::is_same_v<Type, std::unique_ptr<typename Type::element_type, typename Type::deleter_type>> //Typeがstd::unique_ptr<int>の場合true std::is_same_v<Type, std::unique_ptr<typename Type::element_type[], typename Type::deleter_type>> //Typeがstd::unique_ptr<int[]>の場合true の論理和のみで両方の型に対応できるconceptが作れるので わざわざrequiresでメンバを列挙するより 遥かに有効だなと思いました。 あと質問なのですが concept IsSharedPtr と concept IsWeakPtr の定義には 入力されたスマートポインタの要素型が配列のときの処理が記載されてませんが なにか特別な意味が有るのなら教えていただけたら幸いです。 省略していらっしゃるだけかもしれませんが 別解のコードのままではshred_ptw<int[]>とweak_ptr<[]>を identificationに入力したら両方etcと標準出力されましたが template<typename Type> concept IsSharedPtr = // std::shared_ptrか判別 std::is_same_v<Type, std::shared_ptr<typename Type::element_type>> // テンプレート引数が配列型以外の場合 || std::is_same_v<Type, std::shared_ptr<typename Type::element_type[]>>; // テンプレート引数が配列型の場合 template<typename Type> concept IsWeakPtr = // std::weak_ptrか判別 std::is_same_v<Type, std::weak_ptr<typename Type::element_type>> // テンプレート引数が配列型以外の場合 || std::is_same_v<Type, std::weak_ptr<typename Type::element_type[]>>; // テンプレート引数が配列型の場合 と直したら正常な挙動をしました。 それとおまけのコードで > noexceptは適切でない と仰っていますが それはひょっとかして短い関数でどう見てもtry-catch文がないので必要ないという意味ですか。 恐らく私が間違えているかと思われますが 私はnoexceptを自分の作った関数やメソッドを 使う側のプログラマーが目視で例外を飛ばすか 確認するための機能としか認識していません。 (確かにstd::vectorの要素に使うクラスのムーブコンストラクタは noexceptでなければムーブできないので関数やメソッドが noexceptであるかチェックして処理に反映する方法はあるはずですが これといったサイトや資料に出会えていないのでわからないです) あと引数の型をautoにしてその前方にconceptで 型制約できることも知らない優れた書き方 だなと感じたので今後私のコードに取り込んでいけたらなと考えております。 修正後のコードは質問文に追記しておきます。 長文失礼しました。 どうか御返事お待ちしております。
yohhoy

2020/08/05 09:57 編集

> concept IsSharedPtr と concept IsWeakPtr の定義には入力されたスマートポインタの > 要素型が配列のときの処理が記載されてませんがなにか特別な意味が有るのなら こちらは考慮漏れでした。diodeさん対処で適切と思います。 > それとおまけのコードで noexceptは適切でない と仰っていますが > それはひょっとかして短い関数でどう見てもtry-catch文がないので必要ないという意味ですか。 いいえ。 ストリーム出力処理では例外送出される可能性があるため、関数に noexcept 指定がなされていると万一例外が送出されたときプログラムは異常終了(std::terminate)します。 この挙動が意図通りであればよいのですが、大抵は好ましくないはずなので「適切でない」としました。 https://cpprefjp.github.io/reference/exception/terminate.html > 私はnoexceptを自分の作った関数やメソッドを > 使う側のプログラマーが目視で例外を飛ばすか > 確認するための機能としか認識していません。 C++の noexcept は単なるラベルではなく、ずっと強い意味を持っています。プログラマとコンパイラの間にある "契約" の一種です。 ・関数を実装する場合、noexcept を付ける=この関数からは決して例外送出されないことを実装者自身が確信・保証できる場合にのみつけてください。(この "契約" に違反して例外送出されると、C++仕様に従いコンパイラによって異常終了するプログラムが生成されます) ・関数を利用する場合、noexcept が付いている=その関数から決して例外送出されてこないことを安心して利用できます。(前述の通り、万一の場合はプログラム異常終了してしまうため、やはり例外送出されてきません) https://cpprefjp.github.io/lang/cpp11/noexcept.html
退会済みユーザー

退会済みユーザー

2020/08/05 19:48 編集

yohhoyさん 御返事誠に有難うございます。 > ストリーム出力処理では例外送出される可能性があるため... std::coutが例外を飛ばす事があるとは知りませんでした。 教えて下さりありがとうございます。 仰る通りstd::coutが例外を飛ばすのならば identificationをnoexceptにするのはおかしいですね。 確認しましたが確かにここに書いてある関数群には一つもnoexceptは書いてないですね... https://cpprefjp.github.io/reference/ostream/basic_ostream/op_ostream.html 今後もっと注意してnoexcept使って行きます。 しかしその次のstd::terminateの話の中で疑問が生まれました。 yohhoyさんが掲示してくださったstd::terminateのリファレンス の中に > noexceptまたはnoexcept(trueに評価される定数式)が 指定されている関数で例外送出により脱出しようとした場合 terminate()関数が呼び出される。 と書かれているのでその逆(noexcept(false))ではterminate()関数が呼び出されない。 と私は解釈して https://wandbox.org/permlink/D6jQqFQDxTFUXdQ7 上記のコードを作成して実行してみましたが コード中のfunc()noexcept(true)でも func()noexcept(false)でもどちらも 異常終了が発生しました。 やはり私の解釈は間違えているのでしょうか。 あとyohhoyさんの回答もとても有効で簡潔に書ける 素晴らしい方法だなと思ったので 出来ることならyohhoyさんにもベストアンサーを付けたいところですが すでにベストアンサーは付いておりますので 回答の高評価押しておきます。 どうか御返事お待ちしております。
yohhoy

2020/08/06 03:16

> コード中のfunc()noexcept(true)でもfunc()noexcept(false)でもどちらも異常終了が発生しました。 「どこでstd::terminate」が呼ばれるかが異なります。 func()を呼び出すmain関数で例外catchを行わなければ、プログラム全体としては結局異常終了してしまいます。 https://wandbox.org/permlink/8awUSW0EXQ9mvMhR (ベストアンサーはお構いなくw)
退会済みユーザー

退会済みユーザー

2020/08/06 15:43 編集

yohhoyさん 確かに掲示していただいたコードで noexcept関数内から例外を投げた時のみ 異常終了が発生することを確認できました。 noexceptはただの目印では無いことが よくわかったのでとても良かったです。 的確で分かりやすい御回答誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問