私はここでこのトピックに関する素晴らしい記事を見つけました、そして10ページで、著者は2つの重要なことに注意します:
ポイント1は、SOでこのソリューションとして見つけることができます。著者は(印刷された記事の10ページ)、イネーブラーを関数の引数として使用することも可能であると述べています。これは私がやろうとしたことですが、その例ではADLが失敗しました。
ポイント2は、SFINAEに依存し、関数テンプレートセットを減らしています。イネーブラーが失敗した場合、SFINAEはエラーを報告せず、パラメーターの推定により、最適なテンプレートが選択されます。
そうは言っても、前の例を変更しました。
#include <type_traits>
#include <iostream>
template<typename Type>
struct tag {};
struct Atag {};
struct Btag {};
template<typename Type, typename Tag>
struct tag_enabled
{
static const bool value = std::is_same
<
typename tag<Type>::type,
Tag
>::value;
typedef typename std::enable_if
<
std::is_same
<
typename tag<Type>::type,
Tag
>::value,
Type
>::type type;
};
template<typename A, typename B>
typename std::enable_if
<
tag_enabled<A, Atag>::value &&
tag_enabled<B, Btag>::value,
A
>::type
worker(
B const & b
)
{
A result;
std::cout << "Btag -> Atag" << std::endl;
return result;
}
template<typename A, typename B>
typename std::enable_if
<
tag_enabled<A, Atag>::value &&
tag_enabled<B, Atag>::value,
A
>::type
worker(
B const & b
)
{
A result;
std::cout << "Atag -> Atag" << std::endl;
return result;
}
struct test_a {};
struct test_b {};
template<>
struct tag<test_a>
{
typedef Atag type;
};
template<>
struct tag<test_b>
{
typedef Btag type;
};
int main()
{
test_a ta1;
test_b tb1;
auto ta2 = worker<test_a>(ta1);
auto ta3 = worker<test_a>(tb1);
return 0;
}
私はこれが記事で説明されているように機能することを期待していました:
イネーブラーは、AがAtagでタグ付けされ、BがAtagまたはBtagでタグ付けされているかどうかを確認しようとします。イネーブラーは、他の関数テンプレートで有効な場合、一方の関数テンプレートで有効な戻り値の型を生成できません。
tag_enabled<A, Atag>::value && // Check if A is tagged with Atag
tag_enabled<B, Btag>::value, // Check if B is tagged with Btag
A
1つの構成に対して有効な戻り値の型を生成できなくても、エラーにはなりません:SFINAE。次に、コンパイラは別のテンプレートを試行し、BがBtagでタグ付けされている場合function<test_a>(tb1)
、のように、有効な関数をインスタンス化して使用します。
ただし、機能していないようで、次のようなエラーが発生します。
test-template.cpp: In instantiation of ‘struct tag_enabled<test_a, Btag>’:
test-template.cpp:33:33: required by substitution of ‘template<class A, class B> typename std::enable_if<(tag_enabled<A, Atag>::value && tag_enabled<B, Btag>::value), A>::type worker(const B&) [with A = test_a; B = test_a]’
test-template.cpp:86:34: required from here
test-template.cpp:27:13: error: no type named ‘type’ in ‘struct std::enable_if<false, test_a>’
>::type type;
^
test-template.cpp: In instantiation of ‘struct tag_enabled<test_b, Atag>’:
test-template.cpp:51:33: required by substitution of ‘template<class A, class B> typename std::enable_if<(tag_enabled<A, Atag>::value && tag_enabled<B, Atag>::value), A>::type worker(const B&) [with A = test_a; B = test_b]’
test-template.cpp:87:34: required from here
test-template.cpp:27:13: error: no type named ‘type’ in ‘struct std::enable_if<false, test_b>’
イネーブラーを使用することの全体的なポイントがSFINAEに依存することであるとしても、両方の関数テンプレートが有効な選択肢として残っているように思われますか?つまりはそうだ:
typename std::enable_if<(tag_enabled<A, Atag>::value && tag_enabled<B, Atag>::value), A>::type worker(const B&) [with A = test_a; B = test_b]
私はそれtag_enabled<test_b, Atag>::value
が間違っていることを知っています、それは私が期待していることです、enable_if
そしてそれからtypedefがtype
定義されていません。これが、イネーブラーを使用して無効な戻り型を生成し、関数セットを減らすという原則全体のポイントです。
これは実際に著者がadvance
記事の例に対して行っていることです。この例の唯一の違いは、関数テンプレートが2つのパラメーターを取り、両方が何らかの方法でタグ付けされているかどうかがチェックされていることです。Oo私は再び明白な何かを逃しましたか?
SFINAEは、テンプレート引数の置換中に機能します(関数テンプレートのタイプまたはクラステンプレートの特殊化のいずれかを推測する場合)。
一方、クラス定義内にロジックを配置しました。手遅れです。テンプレートがインスタンス化され、引数の型がすでに推定されている場合にのみチェックされ、ハードエラーがトリガーされます。
ただ、取り除くtypedef
の内部tag_enabled
。あなたはそれを必要とせず、あなたはvalue
渡すメンバーだけを必要としますenable_if
(それは置換の失敗が起こるはずです-enable_if
最初のパラメーターの値に基づいてネストされたtypedefを持っているか持っていないでしょう)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加