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

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

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

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

Q&A

解決済

1回答

500閲覧

decltypeのSFINAEについて

__ook

総合スコア49

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

0グッド

1クリップ

投稿2020/03/06 01:30

cpp

1template < class T> 2auto f(T&& range) -> decltype(begin(range), end(range), true) 3{ 4 for (const auto& r : range) { cout << r << endl; } 5 6 return true; 7} 8 9int main() { 10 vector<string> vec{ "test", "items", "range" }; 11 12 f(vec); 13 14}

以上のコードの挙動を説明すると、
decltypeでまずbegin、endが評価されたあとtrueが評価され戻り値はbool、begin、endが失敗した場合実体化されない、で正しいでしょうか。
参考: https://qiita.com/_EnumHack/items/92e6e135174f1f781dbb

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

その通りです。

SFINAEって何か特殊な雰囲気が漂いますが、要するにオーバーロードと考え方は同じです。
void foo(int x);void foo(char const* x); がある時、foo("abc");で呼び出すと前者は本来エラーです。ですが、他にマッチするオーバーロードがあればエラーにせず単に採用しないだけです。SFINAEとよく似た動作ですね。

試しに後者を定義しなければアンマッチ・エラーになります。SFINAEを使う場合も同様でマッチするテンプレートがなければアンマッチ・エラーとなります。

C++

1#include <string> 2#include <vector> 3#include <iostream> 4using namespace std; 5 6template < class T> 7auto f(T&& range) -> decltype(begin(range), end(range), true) 8{ 9 for (const auto& r : range) { cout << r << endl; } 10 11 return true; 12} 13void foo(int) {} 14//void foo(char const*) {} 15 16int main() { 17 vector<string> vec{ "test", "items", "range" }; 18 19 f(vec); 20 21 f(123); 22 foo("abc"); 23}

wandbox

投稿2020/03/06 03:28

Chironian

総合スコア23272

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

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

Chironian

2020/03/06 04:47

そのリンク先は長すぎて全部読む気にはなれないのですが、「ジェネリックな関数テンプレートと非テンプレートのオーバーロードを共存」の意味は理解できませんでした。 なお、関数テンプレートと同じ名前の通常関数を共存させることは、普通に推奨されている使い方です。 https://teratail.com/questions/97314
__ook

2020/03/06 05:04

良いのですね…かなり理解できました。 むしろ明示的特殊化し特定の型に絞るのであれば単にオーバーロードすべきなのですね…ありがとうございました。
__ook

2020/03/06 06:23

すみません、だめです、理解した気でいたのですが、 template <typename T> void foo(T); template <typename T> void foo(T*); // overload of foo(T) template <> void foo<int>(int*); // specialisation of foo(T*) foo(new int); // calls foo<int>(int*); でなぜintの特殊化が呼ばれるのかがわかりません。 オーバーロードの解決を優先するのであれば template <typename T> void foo(T*); が呼ばれるのでは…? なぜ特殊化で解決されるのでしょうか。
Chironian

2020/03/06 07:08

template <typename T> void foo(T*); は、通常関数によるオーバーロードではないです。 よりマッチする範囲が狭い方が優先して呼ばれることが多いので、明示的特殊化の方が呼ばれます。 この辺りは、いなむ先生の https://qiita.com/_EnumHack/items/cd904d383588ddb2189f に詳しい解説があります。 通常関数によるオーバーロードする場合は、void foo(int*);を定義して下さい。(テンプレートではないもの)
__ook

2020/03/06 08:18

なるほど…読みます…ありがとうございました。
__ook

2020/03/06 08:30 編集

> よりマッチする範囲が狭い方が優先して呼ばれることが多い そうなると常にtemplate <> void foo<int>(int*); // specialisation of foo(T*) が呼ばれるような気がしますが… bとcを入れ替えるとbが呼ばれるのですね…? >おおまかに次のようになる 1. f(int),f(int const),f(int&)等、intがcvr修飾されたもの 2. f<int>(int)などの特殊化されたテンプレート 3. f(T),などtemplate 4. f(double),f(long)など型変換可能な引数を持つもの 5. f(...)可変長引数 らしいのでやはりtemplate <> void foo<int>(int*); // specialisation of foo(T*)が呼ばれるような… 難しいです、何度も質問してすみません。
Chironian

2020/03/06 09:55

> foo(new int); // calls foo<int>(int*); これは、template <> void foo<int>(int*)が呼ばれているという意味ではないのですか? 何が疑問なのか、良く分からないです。 > bとcを入れ替えるとbが呼ばれるのですね…? bとcは何を指しています?
__ook

2020/03/07 10:16

すみません、bとcはいただいた記事のyohhoyさんの提示コードを指していました… template <typename T> void foo(T); template <typename T> void foo(T*); template <> void foo<int>(int*); だとint*の特殊化が呼ばれ template <typename T> void foo(T); template <> void foo<int>(int*); template <typename T> void foo(T*); と順序を逆にすると template <typename T> void foo(T*); が呼ばれる、というところが理解できていません。 > よりマッチする範囲が狭い方が優先して呼ばれることが多い であれば常にint*が呼ばれるのではないか、と思いました。 あくまで多い、ということでしょうか。
Chironian

2020/03/07 11:14

プライマリー・テンプレートのオーバーロードを解決した後で、明示的特殊化にマッチするかどうか判定されます。 (a)(b)(c)と記述すると、(c)は(b)の明示的特殊化となり、f(int*)は(b)のプライマリー・テンプレートにマッチした後、より範囲の狭い明示的特殊化の(c)が選択されます。 (a)(c)(b)と記述すると、(c)は(a)の明示的特殊化となり、f(int*)は(b)のプライマリー・テンプレートにマッチしますが、(b)に明示的特殊化がないので(b)が選択されます。 このように明示的特殊化は予測しづらい振る舞いをするので、通常関数のオーバーロードを使ったほうが好ましいです。 ところで、「明示的特殊化」と「完全特殊化」は同じ意味で使われています。
__ook

2020/03/07 11:53

なるほど…理解できました、ありがとうございます。 質問にお付き合いくださったおかげでとても理解が進みました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問