C ++检测是否可以使用模板类型调用类型

C++ detect if a type can be called with a template type

我正在开发一个程序,其中一些数据是静态分配的,一些是动态分配的。现在我想要另一种类型,可以使用该类型的任何模板作为参数调用。

#include <array>
#include <vector>
template <int size> class Foo {
  std::array<int, size> data;
public:
  int& operator[](std::size_t idx) {return data[idx];}
};
template <> class Foo<-1> {
  std::vector<int> data;
public:
  int& operator[](std::size_t idx) {return data[idx];}
};
// option 1- polymorphism
struct FooCaller {
  virtual void operator()(Foo data) = 0; // how would I make this work with both forms of Foo?
};
// option 2- generic programming
template <class T> concept CanCallFoo = requires (const T& t) {
  t(std::declval<Foo&>()); // how do I ensure that this can call any overload of Foo?
};

这两种方法都可以,但我不确定该怎么做。因为完整的代码比较复杂,所以我不想让两个 Foos 都从一个基类继承。

一个可调用的 F 可以写一个限制,它可以被 Foo<x> 调用,这样 x 的任意函数必须为真才有效。

为了使“可以用任何 Foo 调用”测试工作,您必须在编译时反转任意函数。

除了检查 x 的所有 2^32 个可能值之外,没有其他实用的方法可以做到这一点。不完全是。您遇到的问题是 F 类型可能过于强大,您无法确定其属性。这与 Halt 和 Rice 定理以及模板元编程和 C++ 重载解析是图灵完备的事实有关(仅相关,因为 2^32 是有限的)。

在其他情况下,您可以键入擦除。编写一个类型 RefAnyFoo/AnyFooValue,它可以从任何 Foo 类型构造,并“类型擦除”您想要的操作,例如 std::function<void(int)> 类型擦除的方式。它可以回避键入 Foo 或实际限制为从模板创建的类型实例。

然后你的界面是一个需要 AnyFooValue/RefAnyFoo 的界面(取决于你是在谈论副本还是引用)。

现在,将其翻转过来,您可以编写一个接受 F 并接受 RefFooAny 的概念。这不是您所要求的,但您可能遇到了 X/Y 问题;你遇到了一个真正的问题,提出了不完整的解决方案,然后询问如何让你的解决方案起作用,而不是原来的问题。

同样,您可能只关心 Foo<?> 的某些 ducktype 属性,而不关心具体类型。然后一个检查那些鸭子类型的概念可以被你的可调用对象使用;这又不能解决你的问题,就像它把它颠倒过来一样,因为你无法验证一个可调用对象是否接受来自可调用对象之外的整个概念。