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

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

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

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

Q&A

解決済

3回答

581閲覧

std::enable_if の有無

SaitoAtsushi

総合スコア5444

C++

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

1グッド

6クリップ

投稿2018/09/20 08:30

環境

処理系は clang 6.0.1 と gcc 7.3.0 の 32 ビット版で、 MSYS2 上で使っています。

問題

using を用いて二次元配列型の別名 matrix を定義したのですが、念のために要素の数として与えられた数値が正の数かどうかを std::enable_if でチェックするようにしたところ、動作が変わってしまいました。

どうしてこのような差が生まれるのでしょう?

ソースコード

具体的にいうと、以下のコードでマクロで分岐している箇所を切り替えたような場合です。

cpp

1#include <iostream> 2#include <type_traits> 3 4#ifndef ENABLE_SIZE_CHECKING 5template<class T, std::size_t Y, std::size_t X> 6using matrix = T[Y][X]; 7#else 8template<class T, size_t Y, size_t X> 9using matrix = typename std::enable_if<(Y>0)&&(X>0), T[Y][X]>::type; 10#endif 11 12template<class T, size_t Y, size_t X> 13std::ostream& operator<<(std::ostream& os, const matrix<T, Y, X>& mat) { 14 for (auto& i: mat) { 15 for (auto j: i) os << j << ','; 16 os << std::endl; 17 } 18 19 return os; 20} 21 22int main() { 23 matrix<int, 3, 3> mat; 24 25 for (auto& line: mat) for (auto& element: line) element = 10; 26 std::cout << "array rank is " 27 << std::rank<decltype(mat)>::value 28 << std::endl; 29 std::cout << mat <<std::endl; 30 31 return 0; 32}

実行結果

std::enable_if での判定をしない場合

array rank is 2 10,10,10, 10,10,10, 10,10,10,

std::enable_if での判定をする場合

array rank is 2 0x65fec0

補足

上記のコードでトレイトでの判定も入れていることからわかるように変数は確かに二次元配列として定義されていて、暗黙の型変換が入る余地もないはずなのですが、ポインタとして解釈されているような挙動になっているように思われます。

yohhoy👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

alias template そのものは型ではなく型の別名であるだけです。

よって、

cpp

1template<class T, size_t Y, size_t X> 2using matrix = typename std::enable_if<(Y>0)&&(X>0), T[Y][X]>::type; 3 4template<class T, size_t Y, size_t X> 5std::ostream& operator<<(std::ostream& os, const matrix<T, Y, X>& mat);

cpp

1template<class T, size_t Y, size_t X> 2std::ostream& operator<<(std::ostream& os, const typename std::enable_if<(Y>0)&&(X>0), T[Y][X]>::type& mat);

と同値と考えていいです。

ここで、

typename std::enable_if<(Y>0)&&(X>0), T[Y][X]>::type

は依存型名なので関数テンプレートの推論対象ではないです。

結果、operator<<のオーバーロードはADLの候補に挙がらず、int[3][3]はポインタに変換されてoperator<<が解決されたことにより、先頭ポインタのアドレスが表示されたものと考えられます。

cpp

1template<class T, size_t Y, size_t X> 2using matrix = T[Y][X];

とalias templateを記述したときに想定通りオーバーロードが解決されるのは、
T[Y][X]が依存型名ではないため、推論の対象となるからです。

投稿2018/09/20 17:38

編集2018/09/20 17:42
mitama_rs

総合スコア165

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

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

0

回答ではありませんが、以下のように変更し、 clang 6.0.1 で確認したところ意図通りの出力になりました。

c++

1template<class T, size_t Y, size_t X, std::enable_if_t<(Y>0)&&(X>0), int> = 0> 2using matrix = T[Y][X];

参考までに。

投稿2018/09/20 17:05

k44

総合スコア16

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

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

0

コンパイラは Visual Studio 2017 ですが、同じ現象になりました。

  • std::enable_if で判定した場合、operator<<() のオーバーロード関数は呼ばれていないようです。
  • 配列なので、その値を出力したら先頭アドレスになります。
  • typeid(mat).name() で確認しましたが、どちらの場合もたしかに int[Y][X] になっていました。

以下の関数は、第2引数は配列の参照となっていますが、これは C++ で禁止されているはずです。
なので、エイリアステンプレートを使ったときに、コンパイルエラーにならずにビルドでき、さらにその関数が呼ばれるほうが不思議です。

std::ostream &operator<<(std::ostream &os, const matrix<T, Y, X> &mat)

これは間違いだったため、訂正します。
参照の配列は禁止されているが、配列の参照は合法でした。


EnumHack さんの解説を見ましたが、そういう背景があったのですね。
ADL も今回知ったのですが、C++ は奥が深いです。

投稿2018/09/20 16:57

編集2018/09/21 03:01
tiitoi

総合スコア21956

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

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

yohhoy

2018/09/20 17:07

一点明らかな誤りがありますのでそちらの指摘だけ:”配列の参照”と"参照の配列"は別物です。const T &mat[Y][X]は"参照の配列"となるためNGですが、const T (&mat)[Y][X]は"配列の参照"となり合法です。alias templateが using matrix = T[Y][X]; の場合、 const matrix<T, Y, X> &mat は const T (&mat)[Y][X] と等価であり、これは 「3x3 int配列型への参照型」という合法な型です。
Eki

2018/09/20 17:09

とりあえず C++ が言っているのは「参照の配列がない」ということで、「配列の参照がない」ということではないのでは。コンパイルエラーになる文は、等価な書き換えとしては次のようになるかと。これではコンパイルエラーは出ませんでした。 std::ostream &operator<<(std::ostream &os, const T (&mat)[Y][X])
SaitoAtsushi

2018/09/20 17:09

それは配列の参照ではなく参照の配列になっています。 配列の参照にするには const T (&mat)[Y][X]) というように、括弧で覆う必要があります。
tiitoi

2018/09/21 03:04

ご指摘ありがとうございます。 禁止されているのは array of referece であって、reference to array ではなかったですね。 いろいろ勘違いしてました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問