第一个类将用于私有继承,以确保完全相同的布局。这应该使铸造安全。
#include <iostream>
#include <string>
struct data_base
{
data_base( int i, std::string&& s ) noexcept
: i_{ i }
, s_{ std::move( s ) }
{}
int i_;
std::string s_;
};
在这个简单的示例中,我首先打印int
数据成员,然后打印std::string
数据成员,以获取data<true>
的实例。
template<bool = true>
struct data : private data_base // inherits
{
data( int i, std::string&& s ) noexcept
: data_base( i, std::move( s ) )
{}
void print()
{
std::cout << "data<true> - " << i_ << s_ << '\n';
}
};
但是,data<false>
首先打印std::string
数据成员,然后打印int
数据成员。
template<>
struct data<false> : private data_base
{
void print()
{
std::cout << "data<false> - " << s_ << i_ << '\n';
}
};
示例:
int main()
{
data<true> d{ 5, "abc" };
d.print();
( ( data<false>& )d ).print();
}
演示:http://coliru.stacked-crooked.com/a/8b1262afe23dc0a2
如演示所示,即使打开-fstrict-aliasing
标志,也没有警告。
现在,由于它们具有相同的布局,我认为我可以在两种类型之间进行转换以获得不同类型的静态多态性;没有虚拟函数调用的成本。
这种用法是安全还是我触发了未定义的行为?
答案 0 :(得分:1)
从语言规范中的[expr.reinterpret.cast] / 11,您可以将引用从一种类型转换为另一种类型(如果可以将指针转换为另一种类型)。
使用类布局,两种类型都有一个包含所有数据的公共基类。两个派生类型不添加任何数据成员,也不添加任何虚函数,因此两个类的对象布局将是相同的。
因此,如果您使用reinterpret_cast
,则使用安全。
在这种情况下,这类似于转换为引用基类,然后将该引用转换为另一个派生类。
答案 1 :(得分:1)
它或多或少地描述了here,即所谓的提升突变体成语。
据说(强调我的):
Boost变体习语使用
reinterpret_cast
,并且在很大程度上取决于具有相同数据成员(类型和顺序)的两个不同结构的内存布局是可互换的假设。 虽然C ++标准不保证这个属性,但几乎所有编译器都满足它。此外,如果仅使用POD类型,则突变成语是标准的。
注意:该页面已经过时了,我不知道最近的修订是否改变了上述保证的内容。