我常常被这样的类成员函数的顺序调用所困扰(忽略新用法,它对Qt来说,但它不是严格与Qt相关的)
auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);
我一直觉得这样的代码应该写成简单的指令,而不是一个主导行的项目。
Qt的简单例子:
class {
public:
template<class X>
auto& operator()(X* ptr) {
this->ptr = ptr;
return *this;
}
template<class X, class R, class... Args>
auto& operator()(R (X::* fun)(Args...), Args... args) {
if(ptr == nullptr) {
std::cerr << "Editor can't edit nullptr" << std::endl;
return *this;
}
auto call = std::mem_fn(fun);
call(*static_cast<X*>(ptr), args...);
return *this;
}
template <class X>
operator X*() {
auto* result = static_cast<X*>(ptr);
ptr = nullptr;
return result;
}
private:
void *ptr = nullptr;
} EDITOR;
但我更愿意用简单的单行而不是那样做。所以我写了这样的东西:
newLayout->addWidget(EDITOR(new QSpinBox)(&QSpinBox::setRange,-100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
现在用法:
template<class X>
class EDITOR2 {
public:
EDITOR2(X* ptr) {
this->ptr = ptr;
}
template<class R, class... Args>
auto& operator()(R (X::* fun)(Args...), Args&&... args) {
if(ptr == nullptr) {
std::cerr << "Editor can't edit nullptr";
return *this;
}
auto call = std::mem_fn(fun);
call(*ptr, args...);
return *this;
}
operator X*() {
return ptr;
}
X *ptr;
};
除了不是类型安全的外,这是好方法吗? (我可以忍受)
--- 的修改 的 ---
另一种类型安全的方法是:
newLayout->addWidget(EDITOR2<QSpinBox>(new QSpinBox)(&QSpinBox::setRange, -100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
使用方法:
result = sorted([l.split() for l in sys.stdin.readlines()], key=lambda l: int(l[1]), reverse=True)
print(result)
但这需要每次都重新创建编辑器对象,并添加额外的使用代码。
答案 0 :(得分:2)
让我们一起战斗,
第一个对手,香草逼近:
auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);
优点:
缺点:
spinBox
的主题第二个对手,花哨的方法:
newLayout->addWidget(EDITOR2<QSpinBox>(new QSpinBox)(&QSpinBox::setRange, -100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
优点:
缺点:
wtf
的变体,因为他们不习惯spinBox
不会重复,但类名QSpinBox
现在是根据您对每个点的重视程度,最终的选择是你的,我确实比较了一行中的第二种方法,就像你使用换行符一样,基本上回到你试图修复的原始内容< / p>
在我的拙见中,为这么少的人增加一个班级开销是不值得的,我是那种因原始方法的缺点而烦恼的人,杀手点是可读性损失,很可能会迫使你使用换行几乎意味着你已经完成了所有这一切。
答案 1 :(得分:1)
这有点像fluent interface,除了将一堆命名函数作为构建器之外,您只需使用指向成员的指针。这是一种合理的方法,如果你涉及这种事情(主要是基于意见的),但完全缺乏类型安全是不行的。
无论std::cerr
是不是处理错误的好方法。 throw
或assert
。
你可以改进很多:
template <class T>
struct EditorImpl
{
T* ptr;
template <class F, class... Args>
Editor& operator()(F&& f, Args&&... args)
{
std::invoke(std::forward<F>(f), ptr, std::forward<Args>(args)...);
return *this;
}
T* yield() const {
return ptr;
}
};
template <class T>
EditorImpl<T> Editor(T* ptr) { return EditorImpl<T>{ptr}; }
然后你可以写:
newLayout->addWidget(
Editor(new QSpinBox)
(&QSpinBox::setRange,-100, 100)
(&QSpinBox::setValue, 50)
(&QSpinBox::setSingleStep, 5)
.yield());
虽然如果界面已经流畅,这可能会更好:
newLayout->addWidget(
(new QSpinBox)
->range(-100, 100)
->value(50)
->singleStep(5));
但这意味着要编写一堆新的命名函数,你肯定(可能?)不会这样做。
std::invoke()
是C ++ 17,但可以在C ++ 11中实现。