C++20:强制使用指定初始化器来模拟命名函数参数的使用

4

我非常喜欢使用临时结构体和指定初始化器来模拟为我的函数提供命名参数。

struct Args {
    int a;
    int b;
};

int f(Args a) {
    return a.a + a.b;
}

int g() {
    return f({.a = 1, .b = 2}); // Works! That's what I want.
    return f({1, 2}); // Also works. I want to forbid this, though.
}

然而,这些结构体仍可以通过位置初始化器列表进行初始化。也就是说,您仍然可以调用f({1, 2})

我想禁止这种情况,强制调用者在调用时显式命名参数。在C++20中,我该如何做到这一点?


请注意,f({}) 是另一种可能的误用。 - 0x5453
你是否考虑过使用某种形式的强类型,而不是这样?链接 - Bob__
1个回答

4

最好的方式就是进行代码审查。告诉人们使用指定初始化器。指定初始化器很棒,人们喜欢使用它们。这实际上更多是一个社交问题,而不是技术问题。

如果你真的想要推动一下,你可以先加入一些非常糟糕的数据成员,像这样:

class Args {
    struct Key { explicit Key() = default; };
    struct StopIt { StopIt(Key) { } };

public:
    StopIt asdfjkhasdkljfhasdf = Key();

    int a;
    int b;
};

Args仍然是一个聚合体,我们只是多了这个前导数据成员,其拼写是我随便敲键盘时出现的字符。它有一个默认成员初始化程序 - 不过这个初始化程序对于Args是私有的,只有Args知道如何构造它。

因此,用户不能为该特定成员正确提供一个初始化程序,他们必须依赖于默认成员初始化程序来正确初始化它。而且,由于它排在第一位,他们必须依赖于指定初始化以便能够初始化任何其他成员。

但是...这样写有点傻?只要告诉人们使用指定初始化就可以了。


技术上说,他们可以在这种情况下编写 f({Args().asdfjkhasdkljfhasdf, 1, 2}),但这似乎已经变得很荒唐或者很恶意。


我猜应该在StopIt中使用[[no_unique_address]],但除此之外,这听起来像是解决我的问题的方案。谢谢! - Vogelsgesang
1
此外,通过给 StopIt 提供一个已删除的复制构造函数,可能会阻止 f({Args().asdfjkhasdkljfhasdf, 1, 2}) - Vogelsgesang
1
@Vogelsgesang 你的问题的解决方案是告诉人们使用指定初始化器。这只是为了好玩,实际上不要这样做。 - Barry
1
我认为你也可以在这里使用继承。https://godbolt.org/z/9K548enee - Yakk - Adam Nevraumont
@Yakk-AdamNevraumont 实际上更糟糕,因为你可以写成 foo({}, 1, 2}) - Barry
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接