我有模板类ImageView<Pixel>
,它存储一个指向数据和图像大小的非拥有指针。
我希望保持正确性,因此我与Pixel
和const Pixel
合作:
std::byte * data;
ImageView<char> img(data, width, height);
std::byte const* cdata;
ImageView<char> img(cdata, width, height); // compile error
ImageView<const char> cimg(cdata, width, height);
但当然这会导致像这样的代码出现问题:
void foo(ImageView<const char> const&);
ImageView<char> view;
foo(view); // conversion from ImageView<char> to ImageView<const char> const& required
明显的解决方案是使用构造函数添加implict转换:
template <class = std::enable_if_t<std::is_const_v<Pixel>>>
constexpr ImageView(ImageView<std::remove_const_t<Pixel>> const& other) noexcept
: m_data(other.GetData())
, m_stride(other.GetStride())
, m_width(other.GetWidth())
, m_height(other.GetHeight())
{}
但它在每次转换时都有创建临时的缺点,而ImageView
在大多数64位平台上都是24字节。这种临时性仅与原型不同 - 它们具有完全相同的布局。所以我开始考虑使用reinterpret_cast
和const引用转换运算符:
template <class = std::enable_if_t<!std::is_const_v<Pixel>>>
constexpr operator ImageView<std::add_const_t<Pixel>> const&() const noexcept
{
using ConstImageView = ImageView<std::add_const_t<Pixel>>;
return *reinterpret_cast<ConstImageView const*>(this);
}
它似乎有用,但我不确定最后一个片段的正确性。
整个类的简化(仅省略了一些额外的非虚函数)版本:
template <class Pixel>
class ImageView
{
template <class T, class U>
using copy_const_qualifier =
std::conditional_t<
std::is_const_v<T>,
std::add_const_t<U>,
std::remove_const_t<U>>;
using Byte = copy_const_qualifier<Pixel, std::byte>;
public:
constexpr ImageView(Byte * data, unsigned w, unsigned h, std::size_t s) noexcept
: m_data(data)
, m_stride(s)
, m_width(w)
, m_height(h)
{}
constexpr Byte * GetData() const noexcept { return m_data; }
constexpr std::size_t GetStride() const noexcept { return m_stride; }
constexpr unsigned GetWidth() const noexcept { return m_width; }
constexpr unsigned GetHeight() const noexcept { return m_height; }
protected:
Byte * m_data;
std::size_t m_stride; // in bytes
unsigned m_width; // in pixels
unsigned m_height; // in pixels
};
答案 0 :(得分:4)
是的,reinterpret_cast
无效,您不能将对象强制转换为其他不相关类型的对象。嗯,你可以,但请不要随便访问它。
你可以添加一个转换运算符,而不是禁用隐式构造函数,因为你在非重载解析上下文中使用了SFINAE,因此无法工作(有一些解决方法,比如使条件相关,这将实现相同的目标)。但使用转换运算符更清晰IMO:
operator ImageView<const Pixel>() { return {m_data, m_width, m_height, m_stride}; }
你不必担心副本,编译器很聪明! :)而24字节真的没什么可担心的。
自己查看程序集here。 gcc在-O1
及更高版本上生成相同的代码,用于将ImageView<const char>
和ImageView<char>
传递给foo
,以及用于-O2
以上的clang。因此,如果使用优化进行编译,则完全没有区别。
答案 1 :(得分:1)
即使Public lastCBclicked as String
Private Sub c1_Click()
lastCBclicked = ActiveControl.Name
End Sub
Private Sub c2_Click()
lastCBclicked = ActiveControl.Name
End Sub
Private Sub Commande6_Click()
Select Case lastCBclicked
Case "c1"
DoCmd.OpenForm "FORM1"
Case "c2"
DoCmd.OpenForm "FORM2"
Case Else
'traitement
End Select
End Sub
可隐式转换为const char
,char
和ImageView<char>
是完全不相关的类型,我在这里也一无所知。但遗憾的是,从某种意义上说,ImageView<const char>
是 ImageView<const char>
,可以对其进行修改。
幸运的是,我们有一个工具来为编译器提供一些东西是的东西。这是继承的定义(根据Liskov规则)。就是这样。从ImageView<char>
开始ImageView<const char>
继承可以解决您的大多数问题,这是有道理的:
ImageView<char>
答案 2 :(得分:0)
确实可以使用@YCS's idea来实现所需的行为,但它需要更复杂的代码和const_cast
才能使用m_data
。 const_cast
这里是安全的,因为非const ImageView
的构造函数只接受指向非const数据的指针。
所以现在,我将使用转换构造函数或运算符保留版本。如果我注意到临时工对性能的重大影响,我将回到这段代码:
template <class Pixel>
struct ImageView : public ImageView<const Pixel>
{
constexpr ImageView(Pixel * data) noexcept
: ImageView(data)
{}
constexpr Pixel * GetData() const noexcept
{
const Pixel * data = ImageView<const Pixel>::GetData();
return const_cast<Pixel*>(data);
}
};
template <class Pixel>
struct ImageView<const Pixel>
{
constexpr ImageView(const Pixel * data) noexcept
: m_data(data)
{}
constexpr const Pixel * GetData() const noexcept
{
return m_data;
}
private:
const Pixel * m_data;
};
int main()
{
int * data = nullptr;
const int * cdata = nullptr;
ImageView<int> img(data);
//ImageView<int> img1(cdata); // compile error
ImageView<const int> cimg(data);
ImageView<const int> cimg1(cdata);
auto img2 = img;
auto cimg2 = cimg;
ImageView<const int> cimg3(img);
ImageView<const int> cimg4 = static_cast<ImageView<const int>>(img);
ImageView<const int> cimg5 = img;
img.GetData();
cimg.GetData();
return 0;
}