gpt4 book ai didi

c++ - SFINAE 有选择地纳入成员(member)

转载 作者:太空狗 更新时间:2023-10-29 21:45:18 25 4
gpt4 key购买 nike

我正在尝试编写一个模板类,它可能会或可能不会定义特定的成员函数,具体取决于其模板参数类型。此外,此成员函数的返回类型取决于模板参数成员(如果已定义)的返回类型。

下面是我的代码的一个最小示例

#include <iostream>
#include <type_traits>

template <typename T>
struct has_foo_int {
private:
template <typename U>
static decltype(std::declval<U>().foo(0), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) test_type;
enum { value = test_type::value };
};

template <typename T, bool HasFooInt>
struct foo_int_return_type;

template<typename T>
struct foo_int_return_type<T,false> {};

template<typename T>
struct foo_int_return_type<T,true> {
using type = decltype(std::declval<T>().foo(0));
};

template<typename T>
struct mystruct
{
T val;

//auto someMethod(int i) -> decltype(std::declval<T>().foo(0)) // error: request for member ‘foo’ in ‘std::declval<double>()’, which is of non-class type ‘double’
//auto someMethod(int i) -> typename foo_int_return_type<T,has_foo_int<T>::value>::type // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
template<typename R=typename foo_int_return_type<T,has_foo_int<T>::value>::type> R someMethod(int i) // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
{
return val.foo(i);
}
};

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;

return 0;
}

我希望发生的是代码编译良好并为 ms1.someFunc(41) 输出 42。我还希望,如果有人不小心尝试在 ms2 上调用 someFunc,它将无法编译。

不幸的是,我尝试过的每个替代方案都失败了。第一个和第二个,我想我明白为什么它们不起作用。

我读了here SFINAE 仅适用于模板函数,因此我尝试提供一个虚拟模板参数来计算返回类型,但这也以同样的方式失败。

我显然不理解这里的某些东西,我错过了什么?是否有可能实现我想要做的事情?

谢谢。

附言我正在使用 g++ 4.7.3

P.p.s 我也尝试过 std::enable_if 但得到的结果与我的 foo_int_return_type 结构几乎相同。

最佳答案

这是一种简短、整洁且记录在案的方法来执行您正在尝试的操作,随后解决了一些可能的错误。

#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 .它没有。它只是一种 SFINAE 方法来确定是否 U::foo(ArgType)存在于ArgType 0 是什么是隐式可转换。因此 ArgType可以是任何指针或算术输入,而不仅仅是 int .

2) 你可能没有考虑过 std::declval<U>().foo(0)会满意的如果 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只有当它有正确的签名时才会满足,并且签名将是 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。

关于c++ - SFINAE 有选择地纳入成员(member),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17928583/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com