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

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

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

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

Q&A

解決済

2回答

1674閲覧

このコードがなぜコンパイルできるのかわからない

退会済みユーザー

退会済みユーザー

総合スコア0

C++

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

1グッド

1クリップ

投稿2020/07/26 18:46

編集2020/07/26 21:20

質問内容

タイトルが少し分かりづらくなってしまいすいません。

私は普段テンプレートクラスを作る場合
下記のコード1のようにenable_ifを使い
入力できる型の制限をしていますが。

下記コード2のようにメソッドの定義と実装を分けて
書いてみました。
しかしコンパイルエラーが発生しました。(エラー内容は下記参照)

どうすればよいのかしばらく調べてみて
下記コード3のように書けば
コンパイルエラーもおかしな挙動もなく
動くことが分かりましたが
なぜコード2ではエラーが発生し
コード3では問題なく動くの分かりません。

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

コード1

c++

1// 2// テンプレート引数に数値型以外入れることができない構造体Hoge 3// 4template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> = nullptr> 5struct Hoge{ 6 Hoge()noexcept{ 7 std::cout << "Hogeのコンストラクタ" << std::endl; 8 } 9}; 10 11int main() { 12 Hoge<int> hoge1;//数値型なのでコンパイルエラーは発生しない 13 //Hoge<int*> hoge2;//ポインタ型なのでエラー発生 14 15 return 0; 16 17}

コード2

c++

1// 2// 構造体Hogeのメソッド(コンストラクタ)の定義と実装を分けるコード 3// 注意:本コードはコンパイルエラーが発生します。 4// 5 6#include <iostream> 7#include <type_traits> 8#include <memory> 9 10template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> = nullptr> 11struct Hoge{ 12 Hoge()noexcept; 13}; 14 15template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> = nullptr> 16Hoge<Type>::Hoge()noexcept{ 17 std::cout << "Hogeのコンストラクタ" << std::endl; 18} 19 20int main() { 21 Hoge<int> hoge1; 22 //Hoge<int*> hoge2; 23 24 return 0; 25 26}

コード3

c++

1// 2// コンパイルエラーもおかしな挙動もないコード 3// 4#include <iostream> 5#include <type_traits> 6#include <memory> 7 8template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> = nullptr> 9struct Hoge; 10 11template <typename Type> 12struct Hoge<Type>{ 13 Hoge()noexcept; 14}; 15 16template <typename Type> 17Hoge<Type>::Hoge()noexcept{ 18 std::cout << "Hogeのコンストラクタ" << std::endl; 19} 20 21int main() { 22 Hoge<int> hoge; 23 24 return 0; 25 26} 27

コード2をコンパイルしたときのエラー

terminal

1[コードのパス]/main.cpp:15:19: error: invalid use of incomplete type ‘struct Hoge<Type>’ 2 15 | Hoge<Type>::Hoge()noexcept{ 3 | ^~~~~~~~ 4[コードのパス]/main.cpp:10:8: note: declaration of ‘struct Hoge<Type>’ 5 10 | struct Hoge{ 6 | ^~~~

1つ目のエラーは不完全な型を使うなというエラー
2つ目は‘struct Hoge<Type>’の宣言がおかしいという注意喚起?

開発環境の備考

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

ツールの種類ツールの名前バージョン
コンパイラclang++10.0.0
OSLinux Mint20.0
yohhoy👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

コード2が駄目なのは、そこにはデフォルト引数を書けないという制限があるという単純な理由です。

コード3はその制限に引っかからない形で部分特殊化されているので何も問題はありません。

投稿2020/07/27 02:22

SaitoAtsushi

総合スコア5684

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

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

退会済みユーザー

退会済みユーザー

2020/07/27 17:00

SaitoAtsushiさん ご回答誠にありがとうございます。 リンク先を読んでみましたが A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template that appears outside of the member's class. と書かれており 試しに下記コードをコンパイルしてみて 確かにエラーが発生することを確認しました。 https://wandbox.org/permlink/VZTogG62DPEOMSJG なのでコード2でエラーが発生した理由はわかりましたが コード3がコンパイルできる理由が まだ私の中で理解があやふやです。 私の知る限りでは テンプレートの完全特殊化や部分特殊化は プライマリテンプレートクラス/関数の中身が記載されており テンプレート引数に特定の型が入力された場合のみ 違う処理を行うための機能だと考えています。 しかしコード3のプライマリテンプレート構造体は その構造体を部分特殊化した方の構造体と enable_ifの部分を除けばテンプレート引数は同じです のでHoge<int> hoge;で作られるオブジェクトの 型はプライマリテンプレート構造体なのか 部分特殊化された側の構造体なのか わかりません。 また、プライマリテンプレート構造体だったとしても それは宣言しか行われていないので オブジェクトを作れないはずです。 御返事どうかお待ちしております。
SaitoAtsushi

2020/07/27 18:27

結論から言えば Hoge<int> は特殊化されたものが実体化されます。 特殊化 (部分特殊化) というのはその名前が示す通り「特殊な場合」を表しています。 ですから特殊化は一般的である場合 (プライマリテンプレート) のサブセットとなる範囲をサポートし、呼び出す側では「より特殊化されたもの」を優先的に選択することになっています。 ですから、コード3にたとえば以下のような定義を加えた場合にはこちらが優先されるわけです。 プライマリテンプレートよりは部分特殊化が、部分特殊化よりは完全特殊化の方が優先です。 template <> struct Hoge<int>{ Hoge()noexcept{ std::cout << "Hoge<int>のコンストラクタ" << std::endl; } }; コード3において Hoge<int> は特殊化されたものが展開されるのでプライマリテンプレートは実体化されません。 実体化されないので定義がなくても問題にはなりません。 繰り返しますが、特殊化はプライマリテンプレートの「特殊な場合」です。 Hoge<int*> hoge2; などといったように書いた場合にはこれがプライマリテンプレートの特殊な場合であるという前提が成り立たなくなるのでエラーになるのです。
退会済みユーザー

退会済みユーザー

2020/07/28 09:18

SaitoAtsushiさん 御返事誠にありがとうございます。 > 呼び出す側では「より特殊化されたもの」を優先的に選択することになっています。 上記の部分を知らなかったのでコード3の 動きが全く読めていませんでしたが すっきりしました。 ご回答誠にありがとうございました。
guest

0

コンパイルエラーはSaitoAtsushiさんのおっしゃる通りと思います。
※デフォルト引数はプライマリテンプレートのみ

コード2ですが、enable_ifの部分もテンプレート引数ですので、実装部に引数が足りないと思います。
書くとしたら以下の感じでしょうか。(enable_if部分が名前なしなので、適当な名前(T2)を付けました)

C++

1template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> = nullptr> 2struct Hoge { 3 Hoge() noexcept; 4}; 5 6template <typename Type, std::enable_if_t<std::is_arithmetic<Type>::value, std::nullptr_t> T2> 7Hoge<Type, T2>::Hoge() noexcept 8{ 9 std::cout << "Hogeのコンストラクタ" << std::endl; 10}

#これ、コード示しておきながら自信ないのですが、名前なしのまま書く方法ってあるのでしょうか?


追記
SaitoAtsushiさんに教えてもらったコンセプトで書き直してみました。
全然知らなかったので勉強になりました。めちゃシンプルでいいですね。

C++

1template <typename Type> 2concept arithmetic = std::is_arithmetic<Type>::value; 3 4template <arithmetic Type> 5struct Hoge { 6 Hoge() noexcept; 7}; 8 9template <arithmetic Type> 10Hoge<Type>::Hoge() noexcept 11{ 12 std::cout << "Hogeのコンストラクタ" << std::endl; 13}

投稿2020/07/27 07:48

編集2020/07/27 09:41
FKD

総合スコア268

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

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

SaitoAtsushi

2020/07/27 08:20

C++20 が前提ということなのでいっそコンセプト (というか requires) を使ってしまうのが簡単じゃないですかね。
FKD

2020/07/27 09:46

まったく知らなかったのでググってみたところ、こんなズバリの物が・・・勉強になります。 試したものをコード追記してみました。
退会済みユーザー

退会済みユーザー

2020/07/27 19:48 編集

FKDさん そのような書き方でもコンパイル通るんですね とても勉強になりました。 私はまだc++20のコンセプトについては 理解できていませんが FKDさんの書いてくださったコードは コンセプトについて勉強するとき時の参考にさせていただきます。 ご回答誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問