统一指针,值和智能指针的C++模板

sdg*_*sdh 11 c++ templates pointers

我的真实例子非常大,所以我将使用简化的例子.假设我有一个矩形的数据类型:

struct Rectangle {
  int width;
  int height;

  int computeArea() {
    return width * height;
  }
}
Run Code Online (Sandbox Code Playgroud)

另一种消耗该类型的类型,例如:

struct TwoRectangles {
  Rectangle a;
  Rectangle b;
  int computeArea() {
    // Ignore case where they overlap for the sake of argument!
    return a.computeArea() + b.computeArea();
  }
};
Run Code Online (Sandbox Code Playgroud)

现在,我不想对用户的所有权限制TwoRectangles,所以我想把它作为一个模板:

template<typename T>
struct TwoRectangles {
  T a;
  T b;
  int computeArea() {
    // Ignore case where they overlap for the sake of argument! 
    return a.computeArea() + b.computeArea();
  }
};
Run Code Online (Sandbox Code Playgroud)

用途:

TwoRectangles<Rectangle> x;
TwoRectangles<Rectangle*> y;
TwoRectangles<std::shared_ptr<Rectangle>> z;
// etc... 
Run Code Online (Sandbox Code Playgroud)

问题是如果调用者想要使用指针,函数的主体应该是不同的:

template<typename T>
struct TwoRectangles {
  T a;
  T b;
  int computeArea() {
    assert(a && b);
    return a->computeArea() + b->computeArea();
  }
};
Run Code Online (Sandbox Code Playgroud)

统一我的模板化函数的最佳方法是什么,以便最大量的代码重用于指针,值和智能指针?

Bar*_*rry 22

执行此操作的一种方法是将所有内容封装在内TwoRectangles,如下所示:

template<typename T>
struct TwoRectangles {
  T a;
  T b;

  int computeArea() {
    return areaOf(a) + areaOf(b);
  }

private:
    template <class U>
    auto areaOf(U& v) -> decltype(v->computeArea()) {
        return v->computeArea();
    }

    template <class U>
    auto areaOf(U& v) -> decltype(v.computeArea()) {
        return v.computeArea();
    }
};
Run Code Online (Sandbox Code Playgroud)

您不太可能拥有这两个表达式都有效的类型.但是你总是可以用第二个参数添加额外的消歧areaOf().


另一种方法是利用以下事实:在标准库中已经有一种方法可以调用函数:std::invoke().您只需要知道基础类型:

template <class T, class = void>
struct element_type {
    using type = T;
};

template <class T>
struct element_type<T, void_t<typename std::pointer_traits<T>::element_type>> {
    using type = typename std::pointer_traits<T>::element_type;
};

template <class T>
using element_type_t = typename element_type<T>::type;
Run Code Online (Sandbox Code Playgroud)

template<typename T>
struct TwoRectangles {
  T a;
  T b;

  int computeArea() {
    using U = element_type_t<T>;
    return std::invoke(&U::computeArea, a) + 
        std::invoke(&U::computeArea, b);
  }
};
Run Code Online (Sandbox Code Playgroud)