SFINAE选择性地包括成员
SFINAE to Selectively Include Member
我正在尝试编写一个模板类,该类可能会也可能不会根据其模板参数类型定义特定的成员函数。此外,此成员函数的返回类型取决于模板参数成员的返回类型(如果已定义(。
下面是我的代码的最小示例
#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;
}
我想发生的是代码编译良好并输出 42 用于ms1.someFunc(41)
。我还希望,如果有人不小心尝试在ms2
上调用someFunc
,它将无法编译。
不幸的是,我尝试过的每一个替代方案都失败了。第一和第二,我想我明白为什么它们不起作用。
我在这里读到 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)
是确定U::foo
是否存在并采用单个参数的 SFINAE 方法类型 int
.其实不然。它只是确定是否 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
向mystruct::someMethod
添加const
限定符。如果您不在乎或只想打电话给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
已改编从我的另一个答案和你可以在那里阅读它是如何工作的。
您从后一种方法中获得的SFINAE精度来自一个价格。该方法要求您尝试获取T::foo
的地址,看看它是否存在。但是C++不会给你一个重载的地址成员函数,因此如果T::foo
重载,此方法将失败。
这里的代码将编译(或适当地static_assert
(海湾合作委员会>= 4.7.2 叮当>= 3.2。
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 助记符和指向成员语法的指针
- 用于访问容器<T>数据成员的正确 API
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 使用模板参数还包括 constexpr 成员函数enable_if单独定义和声明模板成员函数
- 重构MFC消息映射以包括完全限定的成员函数指针
- QString:包括非文本类型的静态数据成员的初始化
- 功能指针数组(包括成员功能)投掷模板专业化错误
- Doxygen 不显示如何从其他类调用记录的成员函数(包括构造函数)
- 如何强制依赖静态对象(包括模板成员)的初始化顺序
- 有没有办法控制C++中结构成员(包括位字段)之间的填充
- SFINAE选择性地包括成员
- 检查类型是否具有Boost TTI的成员函数(也包括固有成员函数)
- 可以将c++类的所有成员(包括数据和函数)定义为private吗?