これは、あなたが試みていることを行うための簡潔で整然とした文書化された方法であり、その後に対処された可能性のあるバグがいくつかあります。
#include <type_traits>
/*
Template `has_mf_foo_accepts_int_returns_int<T>`
has a static boolean public member `value` that == true
if and only if `T` is a class type that has a public
member function or member function overload
`int T::foo(ArgType) [const]` where `ArgType`
is a type to which `int` is implicitly convertible.
*/
template <typename T>
struct has_mf_foo_accepts_int_returns_int {
/* SFINAE success:
We know now here `int *` is convertible to
"pointer to return-type of T::foo(0)"
*/
template<typename A>
static constexpr bool test(
decltype(std::declval<A>().foo(0)) *prt) {
/* Yes, but is the return-type of `T::foo(0)`
actually *the same* as `int`?...
*/
return std::is_same<int *,decltype(prt)>::value;
}
// SFINAE failure :(
template <typename A>
static constexpr bool test(...) {
return false;
}
/* SFINAE probe.
Can we convert `(int *)nullptr to
"pointer to the return type of T::foo(0)"?
*/
static const bool value = test<T>(static_cast<int *>(nullptr));
};
template<typename T>
struct mystruct
{
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
T val;
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` == `int` and `has_good_foo` == true.
*/
template<typename R = int>
typename std::enable_if<
(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
return val.foo(i);
}
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` != `int` or `has_good_foo` != true.
*/
template<typename R = int>
typename std::enable_if<
!(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
static_assert(has_good_foo::value && std::is_same<R,int>::value,
"mystruct<T> does not implement someMethod(R)");
return i;
}
};
// Testing...
#include <iostream>
struct with_foo_int
{
int foo(int i) {
return i + 1;
}
};
using namespace std;
int main(void)
{
mystruct<with_foo_int> ms1;
cout << ms1.someMethod(41) << endl;
mystruct<double> ms2;
cout << ms2.someMethod(41) << endl; // static_assert failure
return 0;
}
このソリューションは、投稿されたように、あなた自身の試みで考えられるいくつかの抜け穴を忠実に再現します:-
1)評価std::declval<U>().foo(0)
は、存在するかどうかを判断する SFINAE の方法でありU::foo
、 type の単一の引数を取ると信じているように見えますint
。そうではありません。これは、暗黙的に変換可能なものがU::foo(ArgType)
どこに存在するかを判断する SFINAE の方法にすぎません
。したがって、 だけでなく、任意のポインターまたは算術型にすることができます。ArgType
0
ArgType
int
2)のいずれかまたは両方が存在するstd::declval<U>().foo(0)
場合に が満たされるとは考えていないかもしれません。で a を呼び出すか非メンバー関数
を呼び出すかは気にするかもしれません。次のように定義されている場合
:U::foo(ArgType)
U::foo(ArgType) const
const
const
U
with_foo_int
struct with_foo_int
{
int foo(int i) const {
return i + 1;
}
int foo(int i) {
return i + 2;
}
};
次に、指定されたソリューションは非const
オーバーロードを
呼び出し、 ms1.someMethod(41)
== になり43
ます。
2) 扱いやすい。呼び出しのみを実行できるようにする場合は
、修飾子を にT::foo(ArgType) const
追加します。気にしない、または電話したいだけの場合は、そのままにしておいてください。const
mystruct::someMethod
T::foo(ArgType)
1) は、正しい署名T::foo
がある場合にのみ満たされるSNIFAE プローブを作成する必要があり
、その署名が修飾されているかどうかのいずれかになるため、解決するのが少し難しくなります。あなたがしたいと仮定しましょう
。その場合、テンプレート
を次のものに置き換えます。const
int T::foo(int) const
has_mf_foo_accepts_int_returns_int
/* Template `has_mf_foo_arg_int_returns_int<T>
has a static boolean public member `value` that == true
if and only if `T` is a class type that has an un-overloaded
a public member `int T::foo(int) const`.
*/
template< typename T>
struct has_mf_foo_arg_int_returns_int
{
/* SFINAE foo-has-correct-sig :) */
template<typename A>
static std::true_type test(int (A::*)(int) const) {
return std::true_type();
}
/* SFINAE foo-exists :) */
template <typename A>
static decltype(test(&A::foo))
test(decltype(&A::foo),void *) {
/* foo exists. What about sig? */
typedef decltype(test(&A::foo)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0,0)) type;
static const bool value = type::value; /* Which is it? */
};
そしてテンプレートでmystruct
置き換えます:
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
と:
using has_good_foo = has_mf_foo_arg_int_returns_int<T>;
(テンプレートは私の他の回答has_mf_foo_arg_int_returns_int
から適応されており、そこでどのように機能するかを読むことができます。)
後者のアプローチから SFINAE 精度で得られるものには代償が伴います。このアプローチでは、 のアドレスを取得して、T::foo
存在するかどうかを確認する必要があります。ただし、C++ はオーバーロードされたメンバー関数のアドレスを提供しないため、オーバーロードされている場合、このアプローチは失敗しT::foo
ます。
ここのコードはstatic_assert
、GCC >= 4.7.2 clang >= 3.2 でコンパイル (または適切に) されます。