c++ - 如何避免具有相同类型参数的函数中的错误
问题描述
如何避免将相同类型的参数传递给函数时出错?
让我们考虑读取一些二进制数据的函数:
std::vector<uint8_t> read(size_t offset, size_t amount);
很容易将抵消与金额混淆(我做过很多次类似的事情)。
我看到了解决方案:
struct Offset
{
explicit Offset(size_t value) : value{value}{}
size_t value;
};
struct Amount
{
explicit Amount(size_t value) : value{value}{}
size_t value;
};
std::vector<uint8_t> read(Offset offset, Amount amount);
有没有更好的解决方案来避免这样的错误?
解决方案
我能想到两种方法。
标记类型
这基本上就是您在问题中所建议的,但我会一般地实施它。
template <typename Tag, typename T>
struct Tagged
{
explicit Tagged(const T& value) : value{value} { }
T value;
};
template <typename Tag, typename T>
Tagged<Tag, T> tag(const T& value)
{
return Tagged<Tag, T>{value};
}
struct OffsetTag
{ };
struct AmountTag
{ };
using Offset = Tagged<OffsetTag, std::size_t>;
using Amount = Tagged<AmountTag, std::size_t>;
std::vector<uint8_t> read(Offset offset, Amount amount);
这允许您将相同的概念扩展到其他基础数据类型。
命名参数习语
Named Parameter Idiom与@PaulBelanger的回答中的方法有些相似Options
,但它可以就地使用,并且不允许用户使用花括号快捷方式,这会使您回到以前遇到的相同问题。但是,它会默认初始化所有参数,因此虽然您可以避免混淆参数,但它不能强制您为所有参数提供显式值。对于您的示例:
class ReadParams
{
public:
ReadParams() : m_offset{0}, m_amount{128}
{ }
ReadParams& offset(std::size_t offset)
{
m_offset = offset;
return *this;
}
// Could get rid of this getter if you can make the users
// of this class friends.
std::size_t offset() const { return m_offset; }
ReadParams& amount(std::size_t amount)
{
m_amount = amount;
return *this;
}
// Could get rid of this getter if you can make the users
// of this class friends.
std::size_t amount() const { return m_amount; }
private:
std::size_t m_offset;
std::size_t m_amount;
};
std::vector<uint8_t> read(const ReadParams& params);
int main()
{
read(ReadParams{}.offset(42).amount(2048)); // clear parameter names
// read(ReadParams{42, 2048}); // won't compile
read(ReadParams{}.offset(42)); // also possible, amount uses default value
}
如果访问了未初始化的成员,您可以实现ReadParams
asstd::optional
的成员并引发运行时错误;但是您不能再在编译时强制用户实际提供所有参数。
推荐阅读
- graphdb - 通过 REST API 使用相对路径导入服务器文件
- sql - 如何隐藏使用聚合函数的行
- python - 使用 Lorenz 方程的运行时警告
- android - 如何在android中绘制可绘制的圆形边缘矩形
- r - 我在 for() 循环中哪里出错了?
- javascript - 如何使特定输入进入项目中的另一个 .html 文件?
- flutter - Flutter:创建列表
有两个数组(循环) - php - Laravel - 调用我的注销路由时出错
- traefik - Traefik & k3d:仪表板无法访问
- swiftui - .navigationTitle aways 创建自动布局约束冲突