C++ - 将带有引用的长参数列表重构为结构体

标签 c++

我喜欢在调用构造函数后就拥有有效状态的类 - 即所有必需的依赖项都传递到构造函数中。

我还喜欢将必需的依赖项作为引用传递,因为这样在编译时就禁止 nullptr 作为这些参数的值。

示例:

class B;

class A
{
public:
    A(B& b) : b(b) {}

private:
    B& b;
}

实例化 A 后,(几乎)可以保证该实例处于有效状态。我发现这种代码风格非常安全,不会出现编程错误。

我的问题涉及当此类类具有大量依赖项时重构此类类。

示例:

// Includes for B, C, D, E, F...

class A
{
public:
    A(B b, C c, D d, E e, F f) : b(b), c(c), d(d), e(e), f(f) {}

private:
    B b;
    C c;
    D d;
    E e;
    F f;
}

通常,我在结构中放置一长串参数,如下所示:

struct Deps
{
    B b;
    C c;
    D d;
    E e;
    F f;
}

class A
{
public:
    A(Deps deps) : b(deps.b), c(deps.c), d(deps.d), e(deps.e), f(deps.f) {}

private:
    B b;
    C c;
    D d;
    E e;
    F f;
}

这样,它使调用站点更加明确并且更不容易出错:由于所有参数都必须命名,因此您不会因为顺序错误而错误地切换其中两个参数。

遗憾的是,该技术对于引用效果不佳。在 Deps 结构中拥有引用会将问题转发到该结构:然后,Deps 结构需要有一个初始化引用的构造函数,然后该构造函数将有一个很长的参数列表,本质上解决不了任何问题。

现在的问题是:有没有一种方法可以在包含引用的构造函数中重构长参数列表,这样就没有函数会导致长参数列表,所有参数始终有效,并且类的任何实例都不会出现在构造函数中。无效状态(即某些依赖项未初始化或为空)?

最佳答案

鱼与熊掌不可兼得。好吧,除非你使用魔法(也称为更强大的类型)。

让构造函数获取所有必要的依赖项的关键思想是确保在构造发生时提供所有依赖项,并静态地强制执行。如果将此负担转移到结构中,则只有在所有字段都已填充的情况下,才应将该结构传递给构造函数。如果您有未包装的引用,则显然不可能仅部分填充此结构,并且您无法真正向编译器证明您稍后将提供所需的参数。

当然,您可以进行运行时检查,但这不是我们所追求的。理想情况下,我们能够对类型本身已初始化的参数进行编码。这很难以通用方式实现,如果您做出一些让步并为特定类型手动编写它,那么实现起来会稍微容易一些。

考虑一个简化的示例,其中类型在签名中不重复(例如构造函数的签名是 ctor(int, bool, string) )。然后我们可以使用std::tuple表示部分填充的参数列表,如下所示:

auto start = tuple<>();
auto withIntArg = push(42, start);
auto withStringArg = push("xyz"s, withIntArg);
auto withBoolArg = push(true, withStringArg);

我用过auto ,但是如果您考虑这些变量的类型,您会发现它将达到所需的 tuple<int, string, bool>只有在所有这些都被执行之后(尽管以随机顺序)。然后,您可以将类构造函数编写为模板,仅接受确实具有所有必需类型的元组,编写 push功能瞧!

当然,这是很多样板文件,并且可能会出现非常严重的错误,除非您在编写上述内容时非常小心。您想要做的任何其他解决方案都需要有效地做同样的事情; 修改部分填充的参数列表的类型,直到它适合所需的集合。

值得吗?好吧,你自己决定。

关于C++ - 将带有引用的长参数列表重构为结构体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59857141/

相关文章:

c++ - Windows 安装程序,在 C++ 中的自定义操作中访问自定义属性

c++ - 为什么 `std::stringstream::stringstream(std::string&&)` 不存在?

c++ - 在 C++ 中提取从 C++ 类继承的 Python 对象

c++ - 如何使用大 vector 初始化来避免 "compiler limit: compiler stack overflow"?

C++ 运算符重载前缀/后缀

c++ - 使用正则表达式查找和替换

c++ - 在 Optimus 笔记本电脑中以编程方式强制使用 NVIDIA GPU

c++ - 网络 2D 游戏的滞后补偿

c++ - 是否可以使用 std::variant 的 std::variants

c++ - 我如何将缩放合并到这个坐标系中?