"Ambiguous conversion sequence" - 这个概念的目的是什么?



在 N465916.3.3.1 隐式转换序列

10如果存在几个不同的转换序列,每个转换序列将参数转换为参数类型,则与参数关联的隐式转换序列被定义为指定为不明确转换序列的唯一转换序列。为了对隐式转换序列进行排名,如 16.3.3.2 中所述,不明确的转换序列被视为用户定义的转换序列,与任何其他用户定义的转换序列无法区分 [注意:此规则可防止函数因其参数之一的不明确转换序列而变得不可行。如果选择使用不明确转换序列的函数作为最佳可行函数,则调用的格式不正确,因为调用中其中一个参数的转换不明确。

(目前草案的相应部分是12.3.3.1)

此规则的预期目的及其引入的模糊转换序列的概念是什么?

文中提供的注释指出,该规则的目的是"防止函数因其参数之一的转换顺序不明确而变得不可行"。嗯......这实际上指的是什么?可行函数的概念在本文档的前几节中定义。它完全不依赖于转换的歧义(每个参数的转换必须存在,但它们不必是明确的)。而且似乎没有规定一个可行的功能在以后以某种方式"变得不可行"(既不是因为一些歧义,也不是因为其他任何事情)。枚举可行的函数,它们根据某些规则相互竞争成为"最佳",如果只有一个"赢家",则解决方案成功。在这个过程中,一个可行的功能不能(或需要)变成一个不可行的功能。

上述段落中提供的例子不是很有启发性(即不清楚上述规则在该示例中扮演什么角色)。


最初与这个简单示例相关的问题

struct S
{
operator int() const { return 0; };
operator long() const { return 0; };
};
void foo(int) {}
int main()
{
S s;
foo(s);
}

让我们在这里机械地应用上述规则。foo是一个可行的功能。从参数类型S到参数类型int有两个隐式转换序列:S -> intS -> long -> int。这意味着根据上述规则,我们必须将它们"打包"到一个模棱两可的转换序列中。然后我们得出结论,foo最好的可行函数。然后我们发现它使用了我们模棱两可的转换序列。因此,根据上述规则,代码格式不正确。

这似乎毫无意义。这里的自然期望是应该选择S -> int转换,因为它的排名高于S -> long -> int转换。我认识的所有编译器都遵循"自然"重载解决方案。

那么,我误解了什么?

要可行,必须有一个implicit conversion sequence

该标准可能允许多个隐式转换序列,但这可能会使确定选择哪个重载的措辞更加复杂。

因此,标准最终为每个参数定义了一个并且恰好一个隐式转换序列到每个参数。 在存在歧义的情况下,它使用的转换序列是模棱两可的转换序列

完成此操作后,它不再需要处理一个参数到一个参数的多个转换序列的可能性。

想象一下,如果我们在C++写这篇文章。 我们可能有几种类型:

namespace conversion_sequences {
struct standard;
struct user_defined;
struct ellipsis;
}

每个里面都有很多东西(此处跳过)。

由于每个转换序列都是上述序列之一,因此我们定义:

using any_kind = std::variant< standard, user_defined, ellipsis >;

现在,我们遇到了给定参数和参数有多个转换序列的情况。 在这一点上,我们有两个选择。

我们可以为给定的参数、参数对传递一个using any_kinds = std::vector<any_kind>,并确保处理选择转换序列的所有逻辑都处理这个向量......

或者我们可以注意到,对 1 个或多个入口向量的处理从不查看向量中的元素,它被视为一种user_defined转换序列,直到最后我们生成错误。

存储额外的状态并拥有额外的逻辑是一种痛苦。 我们知道我们不需要那个状态,也不需要代码来处理向量。 所以我们只定义一个子类型conversion_sequence::user_defined,代码在找到首选重载后会检查选择的结果重载是否应该生成错误。

虽然C++标准不是(总是)在C++中实现的,并且措辞不必与其实现有1:1的关系,但编写一个强大的标准文档是一种编码,一些相同的问题也适用。

最新更新