【问题标题】:SFINAE on pointers to overloaded functionsSFINAE 关于指向重载函数的指针
【发布时间】:2014-01-12 12:17:30
【问题描述】:

考虑以下程序。我使用h() 作为助手,通过指向来自 cmath 的重载函数的指针来解决歧义:

#include <cmath>

typedef double(*PF1)(double);
typedef double(*PF2)(double, double);
typedef double(*PF3)(double, double, double);
// etc...

auto h(PF1 p) -> decltype(p) {return p;}
auto h(PF2 p) -> decltype(p) {return p;}
auto h(PF3 p) -> decltype(p) {return p;}

int f(int) {return 0;}; // my math function

int main() {
    //auto s_ = std::sin; // won't work as std::sin is overloaded
    auto s = h(std::sin); // works, type of s is a double(*)(double)
    auto p = h(std::pow); // OK.
    // auto my_aim = h(f); // my aim is to make h() work with f too
}

在给定函数本身的指针的情况下,是否有一个更智能或更通用的帮助器来推断指向(可能)重载函数的指针的类型,以便推断将“更喜欢”仅涉及双精度类型的重载(作为返回类型和参数)(如果可用)或其他重载之一。

【问题讨论】:

标签: c++ c++11


【解决方案1】:

以下可能会有所帮助:

constexpr auto h(double(*p)(double)) -> decltype(p) {return p;}
constexpr auto h(double(*p)(double, double)) -> decltype(p) {return p;}
constexpr auto h(double(*p)(double, double, double)) -> decltype(p) {return p;}

template <typename Return, typename ... Args>
constexpr auto h(Return (*p)(Args...)) -> decltype(p) {return p;}

int f(int) {return 0;}; // my math function

int main(int argc, char *argv[])
{
    //auto s_ = std::sin; // won't work as std::sin is overloaded
    auto s = h(std::sin); // works, type of s is a double(*)(double)
    auto p = h(std::pow); // OK.
    auto my_aim = h(f); // works too

    return 0;
}

只要h 参数在一个提供(double(*p)(double..))或没有重载(如f)(因此模板可以推断其类型)。

编辑

添加一个更通用的类来处理这个问题:

template<typename Sign, typename ... Signs>
struct identity : identity<Signs...>
{
    using identity<Signs...>::h;
    static constexpr auto h(Sign p) -> decltype(p) {return p;}
};

template<typename Sign>
struct identity<Sign>
{
    static constexpr auto h(Sign p) -> decltype(p) {return p;}

    template <typename T>
    static constexpr auto h(T p) -> decltype(p) {return p;}
};

让我们在你的例子中使用它:

typedef identity<double(*)(double),
                 double(*)(double, double),
                 double(*)(double, double, double)> MyIdentity;

int f(int) {return 0;}; // my math function

int main(int argc, char *argv[])
{
    auto s = MyIdentity::h(std::sin); // works : double(*)(double)
    auto p = MyIdentity::h(pow);      // works : double(*)(double, double)
    auto my_aim = MyIdentity::h(f);   // works : (int)(*)(int)

    return 0;
}

【讨论】:

  • 您还可以自动生成重载列表h(通过元编程),尽管调用它的语法会略有不同(h 需要是类的静态成员函数模板)。
  • @dyp:添加了您的建议示例。
【解决方案2】:

这是我能做到的最接近你想要做的事情:

#include <cmath>
#include <functional>

template<typename R, typename ...A>
auto h(R f(A...)) -> std::function<R(A...)>
{
    return std::function<R(A...)>(f);
}

int main() {

    auto s = h<double, double>(std::sin);
    auto p = h<double, double, double>(std::pow);
}

你仍然需要指定你要传入的函数的返回类型和参数,但那是因为如果不指定重载会很模糊。

如果你有一个推导出的参数是明确的函数,那就是:

int only_one_overload(int a,  int b) {
    return a + b;
}
auto x = h(only_one_overload);

我建议只使用函数指针:

double (*s)(double) = std::sin;
double (*p)(double, double) = std::pow;

【讨论】:

  • 可能会应用一些 SFINAE 来避免指定类型,至少在一个只有双精度的重载可用的情况下
  • 确实如此。在这种情况下,您可以采用上面的模板化函数并为双精度数提供重载。
【解决方案3】:

Boost 库具有 Boost.Functional/OverloadedFunction 库,旨在将不同的重载包装到单个函数对象中:

boost::overloaded_function<
      const std::string& (const std::string&)
    , int (int)
    , double (double)
> identity(identity_s, identity_i, identity_d);

// All calls via single `identity` function.
BOOST_TEST(identity("abc") == "abc");
BOOST_TEST(identity(123) == 123);
BOOST_TEST(identity(1.23) == 1.23);

我认为您的问题可以通过这个库轻松解决。即使此解决方案不能直接回答您的问题,我认为使用 boost::overloaded_function 是将不同的函数绑定到一个对象并仅通过一个对象调用不同的函数的更好方法,这是您的最终目标,不是吗? (我根据你之前的问题推断出)

【讨论】:

  • 目标是从 cmath 函数制作可调用对象的映射,即 std::map<:string boost::any> = {"sin", boost::any(h (std::sin)) /*...等...*/};因为 boost::any/compiler 不能在没有帮助器的情况下扣除 std::sin 的类型,因为它已经重载了......我会尝试 boost::overload_function 看看它是否有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-08
  • 2016-02-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多