【问题标题】:How to solve function overloading ambiguity in following code如何解决以下代码中的函数重载歧义
【发布时间】:2017-07-30 10:29:36
【问题描述】:

假设intdouble 有函数ovelaods。

void Print(int v)
{
    cout << v << endl;
}

void Print(double v)
{
    cout << v << endl;
}

有一个函数旨在作为可调用的上述函数之一传入。

template<typename FnT>
void Function(int v, FnT&& fn)
{
    fn(v);
}

但是下面的代码是模棱两可的:

Function(1, Print);

Live example

Compiller 无法推断出第二个参数的类型。这很容易解决:

Function(1, static_cast<void(*)(int)>(Print));

我相信存在更通用和优雅的方式来解决这个问题。在 STL 算法中,添加函数重载时可能会出现此问题。

vector<int> v = { 1,2,3 };
for_each(v.begin(), v.end(), Print); // ambigous code

如何巧妙地解决这个问题?

【问题讨论】:

  • 我的意思是,您可以为此滥用模板,但在您的示例中,1)您不需要重载和 2)而不是 std::for_each 使用范围 for 循环。
  • 这只是一个综合示例。当使用任何 STL 算法或任何带有可调用函数作为输入参数的函数时,您可以在任何上下文中面对它。这是我的观点。
  • Have a look here。函数模板也会出现同样的问题。 Vittorio Romeo 的解决方案是通用且简洁的,尽管使用了宏。它也适用于您的情况(事实上,它甚至使用相同的打印示例)。
  • 谢谢。有道理。

标签: c++ templates stl overloading


【解决方案1】:

将其包装在一个函数对象中:

#include <iostream>
#include <utility>

void Print(int v)
{
    std::cout << v << std::endl;
}

void Print(double v)
{
    std::cout << v << std::endl;
}

template<typename FnT>
void Function(int v, FnT&& fn)
{
    fn(v);
}

auto print_wrapper() {
    return [](auto&&...args) -> decltype(auto)
    {
        return Print(std::forward<decltype(args)>(args)...);
    };
}

int main()
{
    Function(1, print_wrapper());
}

从理论上讲,我们在这里所做的是使用绷带来修复损坏的设计。

更好的设计是将Print 的概念定义为模板函数对象。然后我们可以对其进行专门化并根据我们的内心进行定制。

这是一个(非常完整的)模型boost::hash&lt;&gt;,值得任何人花半天时间作为学习练习。

一个简单的例子:

#include <iostream>
#include <utility>
#include <string>

struct Printer
{
    void operator()(const int& i) const {
        std::cout << i << std::endl;
    }

    void operator()(const double& i) const {
        std::cout << i << std::endl;
    }

    template<class Anything>
    void operator()(const Anything& i) const {
        custom_print(i);
    }
};

struct Foo
{

};
void custom_print(Foo const& f) 
{
    std::cout << "a Foo" << std::endl;
}

template<typename X, typename FnT>
void Function(X const& x, FnT&& fn)
{
    fn(x);
}

int main()
{
    Function(1, Printer());
    Function(1.0, Printer());
    Function(Foo(), Printer());
}

【讨论】:

  • 这适用于“打印”。但我认为 OP 想要一个更通用的解决方案。
  • 出于好奇询问:使用参数包 (args) 而不是 print_wrapper() lambda 中的单个参数的任何特定原因(假设 Print 目标是单个参数功能)?或者只是为了笼统,以防 Print 重载被扩展?
  • 谢谢! Print 的好解决方案。缺点是我应该为每个可能重载的函数编写这样的包装器。
  • @Viktor 的诀窍是首先考虑可变函子概念的重载,然后提供重载以满足函子的概念。你正在做的是说,“我有一袋零件 - 我怎么能把它变成一个概念?”。更好的方法是创建一个概念并询问“我需要哪些部分才能使这个概念发挥作用”。
  • @StoryTeller 根据我对 Viktor 的评论。如果我们不得不这样做,那么设计就被破坏了。 Print 的概念应该是一个可以特化的函子。
【解决方案2】:

或者,您可以使用 lambda:

Function(42, [](int i) {Print(i);});.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 2020-10-25
    • 1970-01-01
    相关资源
    最近更新 更多