编译器如何使用策略模式(在结构中使用 overloaded() 或 class)推断类型?

How the compiler infer types using strategy pattern (with overloaded() in struct or class)?

我很难理解在传递“可调用”时,这段代码是如何编译的struct/class。

参考下面的 main 函数,我希望编译器告诉我 Point3D 没有匹配的构造函数(CallableI 不是FuncType).

的实例

我很想做的是在 CallableI 中创建一个 algo 成员并构建引用它的点。但是这个解决方案在很多方面都更好,所以我想了解它是如何工作的。

我有一个基地class Point3D :

template<typename T>
class Point3D
{
typedef std::function<T (const Point3D<T> &p1, const Point3D<T> &p2)> FuncType;
private:
    T x;
    T y;
    T z;
    FuncType algo;
public:
    Point3D(T x, T y, T z, FuncType algo) : x(x), y(y), z(z), algo(algo){}
    T First() const {return x;}
    T Second() const {return y;}
    T Third() const {return z;}
    
    T computeDistance(Point3D<T> &p2){
        return algo(*this, p2);
    }
};

我正在使用一个结构 CallableI 来保存一个特定的函数来调用重载运算符 ()

struct CallableI
{
    CallableI(){};
    
    double operator() (const Point3D<double> &p1, const Point3D<double> &p2){
        double x1{p1.First()}; double x2{p2.First()};
        double y1{p1.Second()}; double y2{p2.Second()};
        return std::abs(x1 - x2) + std::abs(y1 - y2);
    }
};

我正在使用下面的主要函数调用建议的代码:

int main(int argc, char **argv) {
    
    CallableI myCallable;
    Point3D<double> p1(14.3, 12.3, 2.0, myCallable);
    return 0;

std::function 是类型擦除模板。

std::function<R(Arg0, Arg1)>是类型擦除类型。

它可以用你能用的任何值构造:

  1. ()Arg0, Arg1调用,return类型可以转换为R
  2. 可以复制、移动和销毁。

是一种不需要继承的多态

您的 CallballI 满足要求,function<double(Point3D<double> const&,Point3D<double> const&)> 的构造函数完成工作以使其工作。

C++ 中的类型擦除是如何工作的是一个更高级的主题。在 std::function 中,它是如何工作的是未指定的;该标准只是说它有效。您可以通过多种方式自己实现它(反过来可以使用继承),但在您学习 C++ 的这一点上,我建议您不要这样做。

核心思想是 C++ 模板非常强大,std::function 有一个模板构造函数,可以编写胶水代码,让您存储未知但兼容类型的对象,并编写代码来调用 (Point3D<double>, Point3D<double>)就可以了。

template<class Sig>
struct function_view;

template<class R, class...Args>
struct function_view<R(Args...)> {
  void* pstate = nullptr;
  R(*pf)(void*, Args&&...) = nullptr;

  R operator()(Args... args) const {
    return pf(pstate, std::forward<Args>(args)...);
  }

  template<class T>
  function_view( T&& in ):
    pstate( (void*)std::addressof(in) ),
    pf( [](void* pvoid, Args&&...args )->R {
      return (*decltype(std::addressof(in))(pvoid))( std::forward<Args>(args)... );
    })
  {}
};

这是一个半有用的非拥有功能视图的不完整(以多种方式简化)草图。这不是在 C++ 中进行类型擦除的唯一方法,只是一种残酷、简单和简短的方法。

核心是 function_view<Sig> 知道它需要什么,它在构建时保存了如何做,然后忘记了关于传递的类型的所有其他内容(它从内存中“擦除”它)。

然后,当您要求它执行操作时,它会使用这些已保存的操作对类型擦除的数据进行操作。

Live example.

这里,

void foo( function_view<int(int)> f ) {
  std::cout << f(3);
}

foo 不是模板,但可以接受任何支持调用 int 和 return 的不相关类型,可以转换为 int