在 C++ 中实现“有限通用性”

2023-12-27

我正在将一个项目从 Java 转移到 C++,但我在 Java 中相对简单的东西上遇到了问题。

我有课X它是为了处理类型的对象而设计的Y以及继承自的对象Y. X经常需要调用方法Y, say kewl_method(),并且这个方法在继承自的每个类中都是不同的Y。在Java中我可以做这样的事情:

public class X<y extends Y>

我会打电话kewl_method() in X没有任何头痛,它会做我想做的事。如果我理解正确的话(我是 C++ 新手),C++ 中不存在有界通用性这样的东西,所以如果我使用模板X绝对可以用任何东西填充它,我将无法调用kewl_method().

在 C++ 中执行此操作的最佳方法是什么?使用强制转换?

限制:我不能使用 boost 或 TR1。


TravisG https://stackoverflow.com/users/740202/travisg(原:黑蛇)已经回答了 https://stackoverflow.com/a/6803124/14089,就我而言。

但我想对你的问题发表评论:

所以如果我使用带有 X 的模板,则可以用任何东西填充它

不,因为如果没有可访问的,它就无法编译kewl_method.

您必须记住,在 Java 中,有界泛型并不是像您所认为的那样限制泛型类接受的类型,而是为泛型类提供有关其泛型类型 T 的更完整的信息,以便能够验证调用在编译时调用它的方法。

在 C++ 中,此功能按原样提供并由编译器在何处使用:以类似于鸭子类型的方式,但在编译时解析,仅当泛型类型类具有访问权限时,编译器才会接受该方法的编译到kewl_method.

以 4 个类为例:

class X
{
    public : virtual void kewl_method() { /* etc. */ }
} ;

class Y : public X
{
    public : virtual void kewl_method() { /* etc. */ }
} ;

class Z
{
    public : virtual void kewl_method() { /* etc. */ }
} ;

class K
{
    public : virtual void wazaa() { /* etc. */ }
} ;

普通 C++ 解决方案

使用 C++ 模板,您可以提供模板化的类 A:

template<typename T>
class A
{
    public :
        void foo()
        {
            T t ;
            t.kewl_method() ;
        }
} ;

...具有 X、Y 和 Z 类,但不具有 K 类,因为:

  • X:它实现了kewl_method()
  • Y :它公开派生自 X,它实现了kewl_method()
  • Z:它实现了kewl_method()
  • K:它没有实现kewl_method()

...这比 Java(或 C#)的泛型强大得多。用户代码为:

int main()
{
    // A's constraint is : implements kewl_method
    A<X> x ; x.foo() ; // OK: x implements kewl_method
    A<Y> y ; y.foo() ; // OK: y derives from X
    A<Z> z ; z.foo() ; // OK: z implements kewl_method
    A<K> k ; k.foo() ; // NOT OK : K won't compile: /main.cpp error:
                       //   ‘class K’ has no member named ‘kewl_method’
    return 0;
}

您需要致电foo()方法来阻止编译。

如果确实需要约束怎么办?

如果您想将其显式限制为从 X 继承的类,则必须使用代码自行完成(直到C++ 概念 https://secure.wikimedia.org/wikipedia/en/wiki/Concepts_%28C%2B%2B%29已标准化...他们错过了 C++0x 的最后期限,所以我想我们将不得不等待下一个标准...

If you really want to put constraints, there are multiple ways. While I know about it, I'm not familiar enough with the SFINAE https://secure.wikimedia.org/wikipedia/en/wiki/Substitution_failure_is_not_an_error concept to give you the solution, but I can still see two ways to apply constraints for your case (while they were tested for g++ 4.4.5, could someone wiser validate my code?):

添加未使用的演员表?

B类与A类类似,只是多了一行代码:

template<typename T> // We want T to derive from X
class B
{
    public :
        void foo()
        {
            // creates an unused variable, initializing it with a
            // cast into the base class X. If T doesn't derive from
            // X, the cast will fail at compile time.
            // In release mode, it will probably be optimized away
            const X * x = static_cast<const T *>(NULL) ;

            T t ;
            t.kewl_method() ;
        }
} ;

当 B::foo() 被调用时,只有在以下情况下才会编译:T*可以被投射到X*(这只能通过公共继承来实现)。

结果是:

int main()
{
    // B's constraint is : implements kewl_method, and derives from X
    B<X> x ; x.foo() ; // OK : x is of type X
    B<Y> y ; y.foo() ; // OK : y derives from X
    B<Z> z ; z.foo() ; // NOT OK : z won't compile: main.cpp| error:
                       //      cannot convert ‘const Z*’ to ‘const X*’
                       //      in initialization
    B<K> k ; k.foo() ; // NOT OK : K won't compile: /main.cpp error:
                       //      cannot convert ‘const K*’ to ‘const X*’
                       //      in initialization
    return 0 ;
}

但是,作为 A 示例,您需要调用foo()方法来阻止编译。

添加一个自产的“约束”和一个类?

让我们创建一个类来表达对其构造函数的约束:

template<typename T, typename T_Base>
class inheritance_constraint
{
    public:
        inheritance_constraint()
        {
            const T_Base * t = static_cast<const T *>(NULL) ;
        }
} ;

您会注意到该类是空的,并且其构造函数不执行任何操作,因此很有可能它会被优化掉。

您可以像以下示例一样使用它:

template<typename T>
class C : inheritance_constraint<T, X> // we want T to derive from X
{
    public :
        void foo()
        {
            T t ;
            t.kewl_method() ;
        }
} ;

私有继承意味着您的“inheritance_constraint”不会与您的代码发生冲突,但它仍然在编译时表达了一个约束,该约束将停止对不是从 X 派生的类 T 进行编译:

结果是:

int main()
{
    // C's constraint is : implements kewl_method, and derives from X
    C<X> x ; // OK : x is of type X
    C<Y> y ; // OK : y derives from X
    C<Z> z ; // NOT OK : z won't compile: main.cpp error:
             //      cannot convert ‘const Z*’ to ‘const X*’
             //      in initialization
    C<K> k ; // NOT OK : K won't compile: /main.cpp error:
             //      cannot convert ‘const K*’ to ‘const X*’
             //      in initialization
    return 0 ;
}

问题在于它依赖继承和构造函数调用才能有效。

添加一个带有函数的自制“约束”?

这个约束更像是一个静态断言,在调用方法时进行测试。一、约束函数:

template<typename T, typename T_Base>
void apply_inheritance_constraint()
{
    // This code does nothing, and has no side effects. It will probably
    // be optimized away at compile time.
    const T_Base * t = static_cast<const T *>(NULL) ;
} ;

然后使用它的类:

template<typename T>
class D
{
    public :
        void foo()
        {
            // Here, we'll verify if T  inherits from X
            apply_inheritance_constraint<T, X>() ;

            T t ;
            t.kewl_method() ;
        }
} ;

int main()
{
    // D's constraint is : implements kewl_method, and derives from X
    D<X> x ; // OK : x is of type X
    D<Y> y ; // OK : y derives from X
    D<Z> z ; // NOT OK : z won't compile: main.cpp error:
             //      cannot convert ‘const Z*’ to ‘const X*’
             //      in initialization
    D<K> k ; // NOT OK : K won't compile: /main.cpp 2 errors:
             //      ‘class K’ has no member named ‘kewl_method’
             //      cannot convert ‘const K*’ to ‘const X*’
             //      in initialization
    return 0 ;
}

但是,作为 A 和 B 的示例,您需要调用foo()方法来阻止编译。

结论

您必须根据您的具体需求在上述方法中进行选择之一。

但通常情况下,就我而言,我发现这一切都太过分了,我会使用上面第一个更简单的解决方案。

编辑2011-07-24

添加了另一部分代码,以通过简单的函数调用来表达约束。

在“添加未使用的演员?”中部分,我替换了参考演员表X & x = t ;使用指针转换(如其他部分所示),我认为这是更好的。

为了给凯撒应得的,指针转换最初的灵感来自于现在已删除答案 https://stackoverflow.com/questions/6803100/achieving-bounded-genericity-in-c/6803214#6803214 of 乔纳森·格林斯潘 https://stackoverflow.com/users/265530.

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

在 C++ 中实现“有限通用性” 的相关文章

随机推荐

  • 在 AngularJS 中对对象数组进行分组

    基于如下所示的数组 var members name john team 1 name kevin team 1 name rob team 2 name matt team 2 name clint team 3 name will te
  • 如何获得 Firebase 节点的随机子节点?

    首先 这是我的数据库结构 我的目标是从 DE 节点获取一个随机问题对象以便稍后显示它 并且由于没有查询随机子项的内置支持 我必须自己从该迭代器以某种方式获取一个随机对象 目前 我有这段代码 但对如何将其串在一起感到困惑 DatabaseRe
  • 特征和序列化/反序列化

    假设我有两个特征想混合到一个班级中 每个特征都实现类需要的抽象方法 trait Writable def serialize out java io DataOutput trait T1 extends Writable trait A
  • 从POJO获取注释hibernate表名

    我有一个实体 其声明大致如下 Entity Table name myUserTable public class User implements Serializable 我正在创建一个通用 DAO 类 在此过程中我想检索 myUserT
  • 在这种情况下如何获得 foreach 循环之外的值?

    我试图从 mysql 数据中调用 foreach 循环中的所有值 并将该值输入到另一个 mysql select 语句中 看看我下面的代码 它只能收集一个值 我想使用第一个 foreach 循环来包含整个部分 但是 因为 foreach 值
  • 如何调整 GLUT 窗口的大小?

    from OpenGL extensions import alternate from OpenGL GL import from OpenGL GL ARB multitexture import from OpenGL GLU imp
  • 如何使用表达式构建匿名类型?

    在 C 3 0 中 您可以使用表达式创建具有以下语法的类 var exp Expression New typeof MyClass var lambda LambdaExpression Lambda exp object myObj l
  • 使用 QT 的任务计划程序 API

    我想实现一个基于定期日期的任务调度程序 有没有办法使用 QT 的 API 来实现它 另一个问题 我应该指定windows当前用户的用户名和密码吗 如果您想使用 Windows 任务计划程序 则需要使用 COM API http msdn m
  • 如何以编程方式将按钮添加到片段

    我无法设法以编程方式向片段添加按钮 按钮数量可变 我尝试使用 an addView 方法将按钮添加到 rootView 但没有 我尝试在充气之前将按钮添加到布局中 但我不知道如何获取布局 类型 id 错误的预期资源 RelativeLayo
  • 动画结束后我该怎么做?

    我有一个ImageView我用它来通过AnimationDrawable 当我想显示我的进度旋转器时 我这样做 animDrawable start ObjectAnimator ofFloat view alpha 1 0f setDur
  • 当具有该名称的文件已经存在时,如何防止 Django 更改文件名?

    就我而言 我允许用户上传头像图片并使用 user id 作为文件名 简单地说 所以就会有1 jpg 2 jpg等 但是我发现 如果我为某个已上传头像的帐户 假设用户 10 上传新头像 新文件将被命名为 10 1 jpg 这没关系 但是我不需
  • Bootstrap 选项卡中的多个 Google 地图

    我有一个引导选项卡结构 每个选项卡内都有一个谷歌地图 iframe 第一个选项卡的 iframe 看起来不错 但其他选项卡看起来未缩放且未居中 这与 iframe src 代码无关 因为我尝试用第二个和其他代码替换第一个 第一个总是工作得很
  • C++ 我需要在任何地方为函数编写 throw 子句吗?

    Before 考虑有一个类和一个全局函数 例如 这是usefulfuncts hpp void dosome int a int b throw std exception This is usefulfuncts cpp void dos
  • 如何在 SQL 中将表连接到自身并选择最大值

    我有一个contracts table contractId date price partId 1 20120121 10 1 2 20110130 9 1 3 20130101 15 2 4 20110101 20 2 最大的合同dat
  • Bootstrap 轮播与 morris.js 图表冲突?

    我正在使用 morris js 绘制图表 并且我想通过引导轮播显示这些图表 但是如果我这样做 Firefox 将停止响应 它们单独工作可以很好 但如果放在一起就会崩溃 firebug 告诉我有一些与 Raphael 图书馆有关的事情 但我仍
  • 适用于 Android 和 IOS 的 HighChart 库 [已关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 Android 和 iOS 移动应用程序中使用 HighCharts Android 或 iOS
  • 将函数指针“转换”为 Objective-C 中的块

    我正在做一些从 Mono C 到 Obj C 的互操作 并遇到了这个问题 C 代码需要传递回调 它使用函数指针来执行此操作 我可以从 Obj C 端获取函数指针并调用它 一切正常 但我现在需要将该函数指针作为回调提供给第三方 API 该 A
  • Xcode 4.5 - OS X Cocoa 应用程序 - 基本 Web 视图:打开时加载 Google

    我正在尝试创建一个极其基本的 OS X Cocoa 应用程序 该应用程序在打开时会加载http www google com http www google com 尽可能基本 没有后退或前进按钮等 我对 Xcode 4 5 的经验很少 并
  • 如何在反序列化“设置”对象时更新 WPF UI

    我的目标是这样做 用户选择设置文件 读取设置并相应更新 UI 显然 节省也应该是可能的 我的程序当前不是 WPF XAML 现在执行此操作意味着在需要新设置时需要进行大量重复和添加工作 所以有人告诉我 WPF XAML 是正确的选择 我研究
  • 在 C++ 中实现“有限通用性”

    我正在将一个项目从 Java 转移到 C 但我在 Java 中相对简单的东西上遇到了问题 我有课X它是为了处理类型的对象而设计的Y以及继承自的对象Y X经常需要调用方法Y say kewl method 并且这个方法在继承自的每个类中都是不