为什么在 C++ 中对模板施加类型约束是不好的?

2024-01-02

In 这个问题 https://stackoverflow.com/questions/874298/c-templates-that-accept-only-certain-typesOP询问限制模板将接受哪些类。总结一下,Java 中的同等功能很糟糕;并且不要这样做。

我不明白为什么这很糟糕。鸭子打字无疑是一个强大的工具;但在我看来,当一个类看起来时,它会导致令人困惑的运行时问题close(相同的函数名称)但行为略有不同。而且您不一定依赖编译时检查,因为如下示例:

struct One { int a; int b };
struct Two { int a; };

template <class T>
class Worker{
    T data;

    void print() { cout << data.a << endl; }

    template <class X>
    void usually_important () { int a = data.a; int b = data.b; }
}

int main() {
    Worker<Two> w;
    w.print();
}

Type Two会允许Worker编译only if usually_important不被调用。这可能会导致一些实例化Worker编译和其他甚至不在同一个程序中。

不过,在这样的情况下。责任就推到了设计者身上ENGINE确保它是一个有效的类型(之后它们应该继承ENGINE_BASE)。如果不这样做,就会出现编译器错误。对我来说,这似乎更安全,同时不会施加任何限制或增加太多额外的工作。

class ENGINE_BASE {}; // Empty class, all engines should extend this

template <class ENGINE>
class NeedsAnEngine {
    BOOST_STATIC_ASSERT((is_base_of<ENGINE_BASE, ENGINE>));
    // Do stuff with ENGINE...
};

这太长了,但可能会提供一些信息。

Java 中的泛型是一种类型擦除机制,以及类型转换和类型检查的自动代码生成。

templateC++ 中的 s 是代码生成和模式匹配机制。

你可以使用C++template只需付出一点努力就能完成 Java 泛型所做的事情。std::function< A(B) >以协变/逆变方式表现A and B类型和转换为其他std::function< X(Y) >.

但两者的主要设计并不相同。

A Java List<X>将是一个List<Object>上面有一些薄的包裹,因此用户不必在提取时进行类型转换。如果您将其作为List<? extends Bar>,它再次得到一个List<Object>本质上,它只是有一些额外的类型信息,这些信息改变了强制转换的工作方式以及可以调用哪些方法。这意味着您可以从List into a Bar并知道它有效(并检查它)。只为所有生成一种方法List<? extends Bar>.

A C++ std::vector<X>本质上不是一个std::vector<Object> or std::vector<void*>或其他任何东西。 C++ 的每个实例template是不相关的类型(模板模式匹配除外)。实际上,std::vector<bool>使用与任何其他完全不同的实现std::vector(现在这被认为是一个错误,因为在这种情况下,实现差异以令人讨厌的方式“泄漏”)。每个方法和函数都是针对您传递给它的特定类型独立生成的。

在 Java 中,假设所有对象都适合某种层次结构。在 C++ 中,这有时很有用,但人们发现它通常不适合解决问题。

C++ 容器不需要继承公共接口。 Astd::list<int> and std::vector<int>是不相关的类型,但您可以对它们进行统一操作——它们都是顺序容器。

“参数是一个顺序容器吗”这个问题是一个好问题。这允许任何人实现顺序容器,并且这种顺序容器可以与具有完全不同实现的手工编写的 C 代码一样高性能。

如果您创建了公共根std::container<T>所有容器都继承自该容器,它要么装满virtual表格混乱,否则除了作为标签类型之外它毫无用处。作为一种标签类型,它会侵入性地将自身注入到所有非std容器,要求它们继承自std::container<T>成为一个真正的容器。

相反,特征方法意味着对容器(顺序的、关联的等)是什么有规范。您可以在编译时测试这些规范,和/或允许类型注意到它们通过某种特征符合某些公理。

C++03/11 标准库使用迭代器来完成此操作。std::iterator_traits<T>是一个特征类,它公开有关任意类型的迭代器信息T。完全与标准库无关的人可以编写自己的迭代器,并使用std::iterator<...>自动工作std::iterator_traits,手动添加自己的类型别名,或者专门化std::iterator_traits传递所需的信息。

C++11 更进了一步。for( auto&& x : y )可以处理在设计基于范围的迭代之前很久编写的内容,而无需触及类本身。你只需写一个免费的begin and end该类所属的命名空间中的函数返回一个有效的前向迭代器(注意:即使是足够接近的无效前向迭代器也可以工作),然后突然for ( auto&& x : y )开始工作。

std::function< A(B) >是将这些技术与类型擦除一起使用的示例。它有一个构造函数,接受任何可以复制、销毁、调用的内容(B)其返回类型可以转换为A。它可以采用的类型可以是完全无关的——仅测试所需的类型。

因为std::function根据设计,我们可以使用 lambda 可调用函数,它们是不相关的类型,可以将其类型擦除为通用类型std::function如果需要,但当没有类型擦除时,可以从那里知道它们的调用操作。所以一个template采用 lambda 的函数知道在调用时会发生什么,这使得内联成为一个简单的本地操作。

这种技术并不新鲜——它是在 C++ 中出现的std::sort,一种比 C 更快的高级算法qsort由于内联作为比较器传递的可调用对象很容易。

简而言之,如果您需要通用的运行时类型,请输入擦除。如果您需要某些属性,请测试这些属性,不要强制使用公共基础。如果您需要保留某些公理(不可测试的属性),请记录或要求调用者通过标签或特征类声明这些属性(请参阅标准库如何处理迭代器类别 - 再次强调,而不是继承)。如有疑问,请使用启用了 ADL 的自由函数来访问参数的属性,并让默认自由函数使用 SFINAE 来查找方法并调用(如果存在),否则会失败。

这种机制消除了公共基类的中心责任,允许现有类无需修改即可适应您的要求(如果合理),仅在需要的地方放置类型擦除,避免virtual开销,并且理想情况下当发现属性不成立时会生成明显的错误。

If your ENGINE有某些需要传递的属性,编写一个测试这些属性的特征类。

如果存在无法测试的属性,请创建描述此类属性的标签。使用特征类的专门化或规范的 typedef,让类描述该类型适用的公理。 (参见迭代器标签)。

如果你有这样的类型ENGINE_BASE,不要要求它,而是使用它作为所述标签和特征以及公理 typedef 的帮助器,例如std::iterator<...>(你永远不必继承它,它只是充当助手)。

避免过度指定要求。如果usually_important永远不会在你的Worker<X>,可能是你的X不需要b在这种情况下。但要以比“方法无法编译”更清晰的方式测试属性。

有时,只是下注。遵循这些做法可能会让事情变得更困难——所以采取更简单的方法。大多数代码写完后就被丢弃。了解您的代码何时会持续存在,并编写得更好、更具可扩展性和更可维护性。要知道,您需要在一次性代码上练习这些技术,以便在必要时可以正确编写代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在 C++ 中对模板施加类型约束是不好的? 的相关文章

随机推荐

  • 如何加快 Pandas 中最近的搜索速度(也许通过矢量化代码)

    我有两个数据框 每个都包含位置 X Y 和该点的值 对于第一个数据框中的每个点 我想找到第二个数据框中最接近的点 然后找到差异 我的代码可以工作 但它使用 for 循环 速度很慢 关于如何加快速度有什么建议吗 我知道为了性能而摆脱 pand
  • 如何解读Windows任务管理器?

    我运行的是 Windows 7 RC1 它使用与 Vista 相同的 WTM 当我查看流程时 有一些列我不确定有什么区别 内存 工作集 内存 私有工作集 内存 提交大小 谁能告诉我它们是什么 从以下article http cybernet
  • Minimagick 上传期间出现错误“composite -compose Over -gravity North”

    我正在尝试通过以下方式上传图像gem carrierwave gt 1 1 0 and gem mini magick gt 4 7 0 上传图像 我收到这个错误 我正在使用此代码上传图像 MiniMagick Tool Convert n
  • Firefox 与 contextmenu 事件同时触发 click 事件

    接下来的代码记录窗口对象上触发的事件 FIDDLE https jsfiddle net Makha92 kh8roneh 1 var logEvent function var count 1 timer 0 buffer functio
  • 当我在java中加载excel时出现错误

    我正在尝试用 java 读取一个简单的 xlsx private void readExcelData String excel throws Exception FileInputStream file new FileInputStre
  • PayPal REST API 交叉参考交易与付款

    我已成功使用 REST PHP API 完成了 PayPal 付款 但是 我想知道如何将 REST 交易与 PayPal Web 用户界面交叉引用 在 REST 方面 我有付款 ID getID 在交易被批准之前收到 看起来像 PAY 5B
  • Cocos 2d-x 中带有贝塞尔曲线的圆角矩形

    是否可以使用 DrawNode 对象绘制一个圆角矩形 我认为使用贝塞尔曲线是可能的 但我做了一些尝试 但我认为我无法处理它 查看 API 我只发现这两个函数 绘制四边形贝塞尔曲线 const Vec2 origin const Vec2 c
  • RTSP 身份验证:摘要问题

    我需要向流媒体服务器验证我的 RTSP 流 挑战如下 RTSP 1 0 401 Unauthorized WWW Authenticate Digest realm Streaming Server nonce 76bfe6986d3e76
  • 如何使用 Open CV 检测哈欠

    我正在开发一个 iOS 应用程序 需要检测用户何时打哈欠 我所做的是包括 Open CV 并使用 Haar Cascade 查找面孔 然后在面孔内查找嘴巴 也使用 HaarCascade 我遇到的麻烦是 我相信像做 face y mouth
  • UWP 共享功能在 Windows 10 Mobile 中不起作用

    我使用一个按钮创建了一个非常简单的 UWP 应用程序 点击它应该显示内置的共享弹出窗口分享一个PDF file 事实上 我让它适用于 Windows 10 桌面 但不适用于移动设备 弹出窗口不会出现在屏幕上 PDF 文件以字节数组形式出现
  • 保存某个范围内的所有信息并稍后恢复

    有没有办法将字体分配给范围 假设我有一个对象 myFont 我可以写 with Range A1 Font Bold myFont Bold Size myFont Size same with other properties end w
  • 如何通过滚动更改导航栏背景?

    我是网络开发新手 对于我的一个项目 我想在用户滚动时更改导航栏的背景 我希望它看起来像这样 https www nlogic co understanding vlan hopping attacks https www nlogic co
  • Java 问题中的暴力数独求解器算法

    除了求解方法之外 算法中的一切似乎都运行良好 当它使用可解数独板执行程序时 它会说无法解决 我已经尝试了解决方法中我能想到的一切 我尝试过调试 但在测试第一行后失败 有什么建议么 这是到目前为止的完整代码 public class Sudo
  • 我应该如何对具有许多子功能的功能进行单元测试?

    我希望更好地理解我应该测试具有许多子步骤或子功能的函数 假设我有以下功能 Modify the state of class somehow public void DoSomething DoSomethingA DoSomethingB
  • Visual Studio 的 .vsmdi 文件有多重要?

    这是场景 用户 A 已通过 Visual Studio 2010 创建了单元测试 测试项目和单元测试源代码已签入版本控制 用户B 从版本控制中获取测试项目和单元测试源代码 然后 用户 B 打开测试项目并收到一条消息 加载 blah blah
  • 如何正确使用同步链接哈希图

    尝试通过子类化链接哈希映射来制作 lru 映射 地图通过 collections synchronized 运行 映射的所有用法都被同步块包围 如果它们全部被删除 单元测试也会失败 人们可能会认为它们是不必要的 因为地图是通过 collec
  • “react-router”不包含名为“BrowserRouter”的导出

    我正在使用 React router 版本 5 5 1 并尝试在我的index js file src index js 14 8 21 react router does not contain an export named Brows
  • 如何从 Linux 与 SDL 2 对 Windows 进行交叉编译

    我尝试在 Arch Linux 64 位 上使用 SDL 2 和 mingw w64 g 编译器编译一个简单的 C 程序 为此 我从以下位置下载了 SDL2 devel 2 0 4 mingw tar gzhere https www li
  • WPF:如何设置垂直滑块的动态数字范围?

    我目前正在处理一个 WPF MVVM 项目 该项目有一个由多个视图使用的用户控件 但具有不同的值范围 这是我需要的一个例子 正如您所看到的 控件必须根据我需要在滑块中显示的值以不同的行为做出响应 无论数字如何 这只是一个示例 问题之一是该项
  • 为什么在 C++ 中对模板施加类型约束是不好的?

    In 这个问题 https stackoverflow com questions 874298 c templates that accept only certain typesOP询问限制模板将接受哪些类 总结一下 Java 中的同等