- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一个 Result<T>
包含一些 union 的模板类 error_type
和 T
.我想在不求助于虚函数的情况下公开基类中的公共(public)部分(错误)。
这是我的尝试:
using error_type = std::exception_ptr;
struct ResultBase
{
error_type error() const
{
return *reinterpret_cast<const error_type*>(this);
}
protected:
ResultBase() { }
};
template <class T>
struct Result : ResultBase
{
Result() { new (&mError) error_type(); }
~Result() { mError.~error_type(); }
void setError(error_type error) { mError = error; }
private:
union { error_type mError; T mValue; };
};
static_assert(std::is_standard_layout<Result<int>>::value, "");
void check(bool condition) { if (!condition) std::terminate(); }
void f(const ResultBase& alias, Result<int>& r)
{
r.setError(std::make_exception_ptr(std::runtime_error("!")));
check(alias.error() != nullptr);
r.setError(std::exception_ptr());
check(alias.error() == nullptr);
}
int main()
{
Result<int> r;
f(r, r);
}
(这是精简的,如果不清楚,请参阅 extended version)。
基类利用标准布局在偏移量零处找到错误字段的地址。然后它将指针转换到 error_type
(假设这确实是 union 的当前动态类型)。
我认为这是可移植的是否正确?或者它是否违反了一些指针别名规则?
编辑:我的问题是“这是可移植的吗”,但许多评论者对这里使用继承感到困惑,所以我会澄清一下。
首先,这是一个玩具示例。请不要从字面上理解它或假设基类没有用。
设计有三个目标:
Result
类型应该可以通过同质指针或包装器访问。例如:如果不是 Result<T>
我们在谈论 Future<T>
, 应该可以做到 whenAny(FutureBase& a, FutureBase& b)
不管a
/b
具体类型。如果愿意牺牲 (1),这就变得微不足道了。像这样的东西:
struct ResultBase
{
error_type mError;
};
template <class T>
struct Result : ResultBase
{
std::aligned_storage_t<sizeof(T), alignof(T)> mValue;
};
如果我们牺牲 (2) 而不是目标 (1),它可能看起来像这样:
struct ResultBase
{
virtual error_type error() const = 0;
};
template <class T>
struct Result : ResultBase
{
error_type error() const override { ... }
union { error_type mError; T mValue; };
};
同样,理由不相关。我只想确保原始示例符合 C++11 代码。
最佳答案
回答问题:那是可移植的吗?
不可能
详细信息:
如果没有至少 type erasure,这是不可能的(不需要 RTTI/dynamic_cast,但至少需要一个虚函数)。已经有类型删除的工作解决方案 ( Boost.Any
)
原因如下:
你想实例化这个类
Result<int> r;
实例化模板类意味着允许编译器推断成员变量的大小,以便它可以在堆栈上分配对象。
但是在您的实现中:
private:
union { error_type mError; T mValue; };
你有一个变量 error_type
您似乎想以多态方式使用它。但是,如果您在模板实例化时修复了类型,您以后将无法更改它(不同的类型可能具有不同的大小!您也可以强加自己来修复对象的大小,但不要那样做。丑陋和骇人听闻)。
所以你有 2 个解决方案,使用虚函数,或者使用错误代码。
有可能做你想做的事,但你不能那样做:
Result<int> r;
r.setError(...);
具有您想要的确切界面。
有很多可能的解决方案,只要你允许虚函数和错误代码,为什么你不想在这里使用虚函数?如果性能很重要,请记住“设置”错误的成本与设置指向虚拟类的指针一样多(如果没有错误,则不需要解析 Vtable,无论如何模板代码中的 Vtable 是大多数时候可能会被优化掉)。
此外,如果您不想“分配”错误代码,您可以预先分配它们。
您可以执行以下操作:
template< typename Rtype>
class Result{
//... your detail here
~Result(){
if(error)
delete resultOrError.errorInstance;
else
delete resultOrError.resultValue;
}
private:
union {
bool error;
std::max_align_t mAligner;
};
union uif
{
Rtype * resultValue;
PointerToVirtualErrorHandler errorInstance;
} resultOrError;
}
您有 1 个结果类型,或 1 个指向具有所需错误的虚拟类的指针。您检查 bool 值以查看当前是否有错误或结果,然后您从 union 中获得相应的值。虚拟成本仅在出现错误时才支付,而对于常规结果,您只需支付 bool 检查的罚金。
当然,在上面的解决方案中,我使用了一个指向结果的指针,因为它允许通用结果,如果您对基本数据类型结果或仅具有基本数据类型的 POD 结构感兴趣,那么您也可以避免对结果使用指针。
注意在你的情况下std::exception_ptr
确实已经进行了类型删除,但是您丢失了一些类型信息,要再次获取丢失的类型信息,您可以自己实现类似于 std::exception_ptr
的东西但有足够的虚方法来允许安全地转换为正确的异常类型。
关于c++ - 从基类访问 union 的公共(public)部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33069102/
经过几个小时的(重新)搜索,我无法想出普通抽象类和使用模板模式之间的可解释区别。 我唯一看到的是: 使用抽象类时,您需要实现所有方法。但是在使用模板方法时,您只需要实现这两个抽象方法。 有人可以向我解
我正在尝试实现一种算法,该算法可找到以下形状给出的外多边形的每个单独边的对应区域。也就是说,1,2 边的相应区域是 [1,6,7,8,2],2,3 边的区域是 [2,8,3] 等等,CCW 或 CW
我正在尝试在派生 self 的 BaseController 类的任何 Controller 上自动设置一个属性。这是我的 Application_Start 方法中的代码。 UnitOfWork 属
我正在使用 mgcv 包通过以下方式将一些多项式样条拟合到一些数据: x.gam smooth$knots [1] -0.081161 -0.054107 -0.027053 0.000001
考虑以下代码: void foo(){ ..... } int main() { int arr[3][3] ; char string[10]; foo();
本书The c++ programming language有这个代码: class BB_ival_slider : public Ival_slider, protected BBslider {
是否有一个 package.json 属性可用于指定模块解析应启动的根文件夹? 例如,假设我们在 node_modules/mypackage/src/file1 中有一个安装。我们要导入的所有文件都
我正在尝试使用聚合函数来实现与 SQL 查询相同的结果: 查询语句: sqldf(" SELECT PhotoID, UserID,
我正在比较使用 LOESS 回归的两条线。我想清楚地显示两条线的置信区间,我遇到了一些困难。 我尝试过使用各种线型和颜色,但在我看来,结果仍然是忙碌和凌乱。我认为置信区间之间的阴影可能会使事情变得更清
给定这段代码 public override void Serialize(BaseContentObject obj) { string file = ObjectDataStoreFold
我正在构建某种工厂方法,它按以下方式将 DerivedClass 作为 BaseClass 返回: BaseClass Factory() { return DerivedClass(); }
当重写 class delegation 实现的接口(interface)方法时,是否可以调用通常从重写函数中委托(delegate)给的类?类似于使用继承时调用 super 的方式。 来自docum
我有一个基类 fragment (如下所示)。我在其他 3 个 fragment 类中扩展了此类,每个类都共享需要在这 3 个 fragment 中访问的相同 EditText。因此,我在基类中设置了
如何在不加载额外库的情况下在 R 中计算两个排列之间的 Kendall tau 距离(又名冒泡排序距离)? 最佳答案 这是一个 O(n.log(n)) 的实现,在阅读后拼凑而成,但我怀疑可能有更好的
情况 我创建了一个具有国际化 (i18n) 的 Angular 应用程序。我想在子域中托管不同的版本,例如: zh.myexample.com es.myexample.com 问题 当我使用命令 n
std::is_base_of 之间的唯一区别和 std::is_convertible是前者在 Base 时也成立是 私有(private)或 protected Derived 的基类.但是,您何
我创建了一个名为 baseviewcontroller 的父类(super class) uiviewcontroller 类,用于包含大多数应用屏幕所需的基本 UI。它包括一个自定义导航栏和一个“自
我是一名优秀的程序员,十分优秀!