如何解决如何使用类的常量裁判和非常量裁判版本避免 DRY?
我有一些看起来像这样的类:
struct A {
void *stuff;
int x;
int foo() const;
}
int bar(A a1,A2 a2);
int baz(A2 a2);
问题是,并非所有这些函数实际上都改变了 a2.stuff
中的任何内存;然而 - 我不能接受一个 const void*
和一个 int
,构造一个 A 并将它传递给这样的函数。或者更确切地说,我可以,但只使用 const_cast<>
这真的不是你的生活方式。另外,我很容易混淆并将我的 const_cast<>
后的 A 传递给实际通过 A::stuff
修改数据的函数。
所以,我决定我想要一个“常数 A”。不是字段不可变的 A - 通过它您不会更改内容指向的 A。
如果 A 以某种方式被模板化,即如果它是某种类型的 T
- 那么没问题,你用 AA<void>
替换 <const void>
并且 Bjarne 是你的叔叔。但是... A 不是模板化的。而且我不想让它模板化,因为我不希望 A<int>
或类似的东西存在。
那我该怎么办?
最简单的方法是复制 A 的定义,几乎,对于 const_A
:
struct const_A {
void const *stuff;
int x;
int foo() const;
}
struct A {
void *stuff;
int x;
int foo() const;
operator const_A() const { return const_A { stuff,x }; }
}
没有大量样板、私有成员、所有ctors 的实现等的解决方案的加分项
解决方法
基于std::experimental::propagate_const
思想的解决方案:
namespace detail {
struct const_propagating_void_ptr {
void* ptr;
operator void *() { return ptr; }
operator void const *() const { return ptr; }
};
} // namespace detail
struct A {
detail::const_propagating_void_ptr stuff_;
int x;
int foo() const;
void * stuff() { return stuff_; }
void const * stuff() const { return stuff_; }
};
优点:
- Rule of Zero(也称为 C++ Core Guideline C.20)FTW。
- 实际上,
A
和类似指针的类只是普通的旧结构! - 您可以在其他地方重用类似指针的类(甚至对其进行模板化),因此它有更多的文本但不是那么多。
缺点:
-
这个compiles without warnings。感谢@AyxanHaqverdili 指出这一点。const A a1 {my_ptr,123}; A a2 {a1}; *a2.stuff() = 456; // ... and a1's pointer is used for write access :-(
- 无法从
const A
和const void*
构造int
(没有 const_cast'ing)。 - 实际上并没有保护 const-propagator 免受直接访问;但至少这种访问必须是明确的。我们可以通过实际使用
std::experimental::propagate_const
来禁用它,它可能有一个受保护的数据成员,覆盖赋值和移动赋值运算符等。
问:为什么不在 A
中使用 getter,而使用 void*
?
答: A
的用户很容易犯错误,访问 A::stuff_
而不是 A::stuff()
。如果这只是一个 void *
,那么意外写入 A::stuff_
的几率会很高。但是使用此解决方案,要获得 void*
,您需要编写:A::stuff_::ptr
,并且没有人会错误地编写 my_a.stuff.ptr
而不是 my_a.stuff()
。
@Eljay 在评论中建议的一个解决方案是让可变 A 继承不可变 A,这是 Objective-C 的常见习语。
也许是这样的:
struct const_A {
const void *stuff_;
int x;
const void* stuff() const { return stuff_; }
int foo() const;
}
struct A : public const_A {
void* stuff() const { return const_cast<void*>(stuff_); }
}
?
缺点:
- 不能再写
A{ &my_stuff,123 }
。
类似于@einpoklum,我正在考虑使用 std::variant
:
#include <variant>
struct const_A {
std::variant<void*,const void*> stuff_;
int x;
int foo() const;
void* stuff() { return std::get<0>(stuff_); }
const void* stuff() const { return std::get<1>(stuff_); }
};
int main(void)
{
return 0;
}
,
这样的东西也可以工作,但模板的东西必须到处传播:
pd.Series([0 for i in range(20)])
,
我之所以要求更改功能的能力,是因为可能有外部非技术原因,您可能不会。
这是我想出的使用很少使用的“使用”语法(至少我几乎从未见过):
struct A {
A(void* stuff,int x) : stuff_(stuff),x_(x) {}
const void* const_stuff() const { return stuff_; }
void* stuff() { return stuff_; }
int foo() const;
private:
void *stuff_;
int x_;
};
struct const_A : private A {
const_A(void* stuff,int x) : A(stuff,x) {}
private:
using A::stuff;
public:
using A::const_stuff;
using A::foo;
};
void doSomethingWith_const_A(const_A& c) {
const void * stuff = c.const_stuff();
// void * v = c.stuff(); //won't work
}
void doSomethingWith_const_A(const const_A& c) {
const void * stuff = c.const_stuff();
//void * v = c.stuff(); //won't work
}
void doSomethingWith_A(A& a) {
void * stuff = a.stuff();
const void * const_stuff = a.const_stuff(); //okay
}
int main(int argc,char* argv[])
{
A Aobj(nullptr,0);
const_A const_Aobj(nullptr,0);
doSomethingWith_const_A(const_Aobj);
doSomethingWith_A(Aobj);
doSomethingWith_A(const_Aobj);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。