下面是一個簡短,整潔和記錄的方式做你正在嘗試, 以及一些可能的錯誤之後解決。
#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
並且採用int
類型的單個參數 。它沒有。它僅僅是一種確定 U::foo(ArgType)
是否存在的SFINAE方式,其中ArgType
是0
是 可以隱式轉換的任何東西。因此ArgType
可以是任何指針或算術 類型,而不僅僅是int
。
2)您可能沒有考慮到std::declval<U>().foo(0)
會滿意 如果或任兩者U::foo(ArgType)
U::foo(ArgType) const
存在。您 可能會在意您是否撥打const
或U
的非const
成員函數,並且您肯定會關心您調用的兩個成員函數中的哪一個。如果 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)是有點難以解決,因爲你必須製作一個SNIFAE探頭 T::foo
這是唯一滿意的,如果它有正確的簽名,那 簽名要麼是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
適用 從my other answer和 你可以閱讀它的工作原理。)
你從SFINAE精度中獲得的後一種方法的價格是 。該方法要求您嘗試取T::foo
, 的地址以查看它是否存在。但C++不會給你超載的成員函數的地址,所以如果T::foo
被重載,這種方法將失敗。
此處的代碼將編譯(或適當地static_assert
)與 GCC> = 4.7.2 clang> = 3.2。
該函數本身需要將相關參數作爲模板參數,否則在調用點沒有替換,並且您沒有獲得SFINAE。一個流行的解決方法是像'template' –
Xeo
它可行!我不確定爲什麼,但如果您將此作爲答案並解釋,我會很樂意接受。 – Dan
對於SFINAE,編譯器(或用戶)需要推導(提供)編譯器稍後將嘗試的類型** S ** ubstitute和** F ** ail –