[英]avoid pointer-to-member-function for non-class type
I am writing a kind of container class, for which I would like to offer an apply
method which evaluates a function on the content of the container. 我正在编写一种容器类,我想提供一个
apply
方法来评估容器内容的函数。
template<typename T>
struct Foo
{
T val;
/** apply a free function */
template<typename U> Foo<U> apply(U(*fun)(const T&))
{
return Foo<U>(fun(val));
}
/** apply a member function */
template<typename U> Foo<U> apply(U (T::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error
The last line yields error: creating pointer to member function of non-class type 'const int'
. 最后一行产生
error: creating pointer to member function of non-class type 'const int'
。 Even though I only instantiated Foo
and not used apply
at all . 即使我只是实例化了
Foo
而没有使用apply
。 So my question is: How can I effectively remove the second overload whenever T
is a non-class type? 所以我的问题是:当
T
是非类型类型时,如何有效地删除第二个重载?
Note: I also tried having only one overload taking a std::function<U(const T&)>
. 注意:我也试过只有一个重载采用
std::function<U(const T&)>
。 This kinda works, because both function-pointers and member-function-pointers can be converted to std::function
, but this approach effectively disables template deduction for U
which makes user-code less readable. 这种方法有效,因为函数指针和成员函数指针都可以转换为
std::function
,但是这种方法有效地禁用了U
模板推导,这使得用户代码的可读性降低。
Using std::invoke
instead helps, it is much easier to implement and read 使用
std::invoke
有助于实现和阅读更容易
template<typename T>
struct Foo
{
T val;
template<typename U> auto apply(U&& fun)
{
return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
}
};
struct Bar{};
template class Foo<Bar>;
template class Foo<int>;
However, this won't compile if the functions are overloaded 但是,如果函数重载,则无法编译
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
The way around that is to use functors instead 解决这个问题的方法是使用仿函数
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
Which also makes it more consistent with member function calls 这也使其与成员函数调用更加一致
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
In order to remove the second overload you'd need to make it a template and let SFINAE work, eg like this: 为了消除第二次过载,您需要将其设为模板并让SFINAE工作,例如:
template<typename T>
struct Foo
{
T val;
//...
/** apply a member function */
template<typename U, typename ObjT>
Foo<U> apply(U (ObjT::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
Alternatively, you could remove the second overload altogether, and use lambda or std::bind:
或者,您可以完全删除第二个重载,并使用lambda或
std::bind:
#include <functional> // for std::bind
template<typename T>
struct Foo
{
T val;
/** apply a member function */
template<typename U, typename FuncT>
Foo<U> apply(FuncT&& f)
{
return {f(val)};
}
};
struct SomeType
{
int getFive() { return 5; }
};
int main()
{
Foo<SomeType> obj;
obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}
How can I effectively remove the second overload whenever T is a non-class type?
当T是非类型类型时,如何有效地删除第二个重载?
If you can use at least C++11 (and if you tried std::function
I suppose you can use it), you can use SFINAE with std::enable_if
如果你至少可以使用C ++ 11(如果你试过
std::function
我想你可以使用它),你可以使用SFINAE和std::enable_if
template <typename U, typename V>
typename std::enable_if<std::is_class<V>{}
&& std::is_same<V, T>{}, Foo<U>>::type
apply(U (V::*fun)() const)
{ return Foo<U>((val.*fun)()); }
to impose that T
is a class. 强加
T
是一个阶级。
Observe that you can't check directly T
, that is a template parameter of the class, but you have to pass through a V
type, a template type of the specific method. 注意,你不能直接检查
T
,这是类的模板参数,但你必须通过V
类型,即特定方法的模板类型。
But you can also impose that T
and V
are the same type ( && std::is_same<V, T>{}
). 但是你也可以强加
T
和V
是相同的类型( && std::is_same<V, T>{}
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.