避免非 class 类型的指向成员函数的指针
avoid pointer-to-member-function for non-class type
我正在编写一种容器 class,为此我想提供一个 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
最后一行产生 error: creating pointer to member function of non-class type ‘const int’
。 尽管我只实例化了 Foo
而根本没有使用 apply
。所以我的问题是:当 T
是非 class 类型时,如何有效地删除第二个重载?
注意:我也试过只有一个重载 std::function<U(const T&)>
。这种方法可行,因为函数指针和成员函数指针都可以转换为 std::function
,但这种方法有效地禁用了 U
的模板推导,这使得用户代码的可读性降低。
为了删除第二个重载,您需要将其设为模板并让 SFINAE 工作,例如。 G。像这样:
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)());
}
};
或者,您可以完全删除第二个重载,并使用 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(); });
}
改用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>;
但是,如果函数重载,这将无法编译
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
解决这个问题的方法是改用函子
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
这也使得它与成员函数调用更加一致
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
How can I effectively remove the second overload whenever T is a non-class type?
如果您至少可以使用 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)()); }
强加 T
是一个 class。
观察不能直接查T
,那是class的一个模板参数,而是要通过一个V
类型,一个模板类型具体方法。
但你也可以强加 T
和 V
是同一类型 (&& std::is_same<V, T>{}
)。
我正在编写一种容器 class,为此我想提供一个 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
最后一行产生 error: creating pointer to member function of non-class type ‘const int’
。 尽管我只实例化了 Foo
而根本没有使用 apply
。所以我的问题是:当 T
是非 class 类型时,如何有效地删除第二个重载?
注意:我也试过只有一个重载 std::function<U(const T&)>
。这种方法可行,因为函数指针和成员函数指针都可以转换为 std::function
,但这种方法有效地禁用了 U
的模板推导,这使得用户代码的可读性降低。
为了删除第二个重载,您需要将其设为模板并让 SFINAE 工作,例如。 G。像这样:
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)());
}
};
或者,您可以完全删除第二个重载,并使用 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(); });
}
改用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>;
但是,如果函数重载,这将无法编译
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
解决这个问题的方法是改用函子
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
这也使得它与成员函数调用更加一致
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
How can I effectively remove the second overload whenever T is a non-class type?
如果您至少可以使用 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)()); }
强加 T
是一个 class。
观察不能直接查T
,那是class的一个模板参数,而是要通过一个V
类型,一个模板类型具体方法。
但你也可以强加 T
和 V
是同一类型 (&& std::is_same<V, T>{}
)。