我的代码中有这种模式:
public:
int start(const uint32_t channel);
int stop(const uint32_t channel);
void set_current_desc(uint32_t channel, uint64_t cdesc);
// ...
private:
const uint32_t channel_;
所有函数都接受参数channel
。我想提供这个接口:
public:
int start(const uint32_t channel);
int start(void) { return start(channel_); }
int stop(const uint32_t channel);
int stop(void) { return stop(channel_); }
void set_current_desc(uint32_t channel, uint64_t cdesc);
void set_current_desc(uint64_t cdesc) { set_current_desc(channel_, cdesc); }
// ...
private:
const uint32_t channel_;
是否有一种通用方法可以一次性对所有函数执行此操作,还是我必须将其全部输入?我猜存在一些混乱的宏方法,但我更喜欢 C++ 非宏解决方案。
编辑:由于向后兼容,初始界面必须保留。
11
最佳答案
3
您可以修改方法以接受具有默认值的。
如果调用者未提供通道(通过新接口),channel_
则将使用该成员。
如果调用者提供了通道(如您为向后兼容所要求的),则将使用该通道。
这是使用方法完成的。
#include <optional>
#include <iostream>
#include <cstdint>
class Test {
public:
Test(uint32_t channel) : channel_(channel) {}
void start(std::optional<uint32_t> channel = std::nullopt)
{
// Determine `cur_channel` to use:
uint32_t cur_channel = channel.value_or(channel_);
// Use `cur_channel` ...
std::cout << "start, using channel: " << cur_channel << "\n";
}
private:
const uint32_t channel_;
};
int main() {
Test test{ 333 }; // `channel_` member will be 333
test.start(); // will use the `channel_` member of `test`
test.start(5); // will use 5 for the current channel (backwards compatibility)
}
输出:
start, using channel: 333
start, using channel: 5
类似的方法也可用于其余的方法。
|
您可以编写具有重载的基类channel_
,但仍然有相对大量的样板。
struct channel_ops {
int start(this auto&& self) { return self.start(channel_); }
int stop(this auto&& self) { return self.stop(channel_); }
void set_current_desc(this auto&& self, uint64_t cdesc) { self.set_current_desc(channel_, cdesc); }
protected:
int channel_;
}
class foo: public channel_ops {
// ...
public:
int start(const uint32_t channel);
using channel_ops::start;
int stop(const uint32_t channel);
using channel_ops::stop;
void set_current_desc(uint32_t channel, uint64_t cdesc);
using channel_ops::set_current_desc;
// ...
};
在 C++23 之前,您需要使用 CRTP:
template<typename Self>
struct channel_ops {
int start() { return static_cast<Self*>(this)->start(channel_); }
int stop() { return static_cast<Self*>(this)->stop(channel_); }
void set_current_desc(this auto& self, uint64_t cdesc) { static_cast<Self*>(this)->set_current_desc(channel_, cdesc); }
protected:
int channel_;
}
class foo: public channel_ops<foo> {
// ...
public:
int start(const uint32_t channel);
using channel_ops::start;
int stop(const uint32_t channel);
using channel_ops::stop;
void set_current_desc(uint32_t channel, uint64_t cdesc);
using channel_ops::set_current_desc;
// ...
};
|
如果坚持这种设计,一种可能性是使用默认的常规参数值UNDEFINED
并依靠它来知道是否start
在没有任何参数的情况下调用。
#include <cstdint>
#include <iostream>
#include <limits>
#include <cassert>
class foo
{
public:
foo (uint32_t channel) : channel_(channel)
{
// UNDEFINED_CHANNEL must be forbidden for channel_
// otherwise infinite recursion could occur in 'start'
assert (channel_ != UNDEFINED_CHANNEL);
}
int start (const uint32_t channel = UNDEFINED_CHANNEL)
{
// if no argument is provided, we use 'channel_'
if (channel == UNDEFINED_CHANNEL) { return start (channel_); }
// do something here
std::cout << "input: " << channel << "\n";
return 0;
}
private:
const uint32_t channel_ {};
static const uint32_t UNDEFINED_CHANNEL = std::numeric_limits<uint32_t>::max();
};
int main()
{
foo x = 123;
x.start ();
x.start (1);
x.start (std::numeric_limits<uint32_t>::max());
foo y = std::numeric_limits<uint32_t>::max(); // we should have an assert here
y.start();
}
无论如何,这并不完美,因为您不应该将其用作UNDEFINED
参数,start
因为它会使用channel_
成员值。
您还必须确保UNDEFINED
不能使用它,否则如果您不提供任何参数,channel_
您可能会出现无限递归。start
|
–
–
std::optional
–
channel_
?也许公开一个 setter 方法来更改其值,那么你可以避免将其传递给每个函数,只需在调用任何函数之前调用 setter 方法(如果需要)。–
channel_
默认参数需要成员是静态的。我不确定我是否愿意这样做。@Yksisarvinen:我同意设计不是最优的,但我希望代码保持向后兼容早期用途。@kiner_shah:channel_
在构造函数的初始化列表中设置。但如上所述,我更愿意保留已经存在的接口以实现向后兼容。–
|