【发布时间】:2019-02-27 09:33:15
【问题描述】:
最小示例程序:
#include <vector>
void f(std::vector<int>) {} // #1
void f(std::vector<void *>) {} // #2
int main() { f({ 1 }); }
这是一个有效的程序直觉上是有意义的:使用重载 #1 的调用将是有效的,使用重载 #2 的调用将是格式错误的,因此应该选择重载 #1。这就是 clang 所做的。
不幸的是,按照标准,这似乎是模棱两可的,因为std::vector<void *> 的构造函数可以用int 调用,方法是将其隐式转换为size_t。在重载决议期间应该忽略构造函数explicit 的事实,如果选择该重载,程序将只是格式错误。 GCC 以模棱两可的方式拒绝该调用,并且这样做看起来是正确的。
我可以通过拼写类型名称来修改代码让 GCC 接受调用:f(std::vector<int>{ 1 });。我也可以使用带有默认参数的标签调度来明确指定要使用的重载,同时允许像以前一样接受现有调用。
这两个都是可以接受的,但是当回到真正的代码时会很快变得相当冗长。是否有另一个选项可以让我避免拼出完整的类型名称,但坚持使用当前的重载?我想了一会儿{ 1, } 可能会起作用,但当然不会,int i = { 1, }; 也完全有效,不能用来避免 #2。
如果有助于排除一些替代方案,实际代码确实涉及std::vector<int> 和std::vector<T>,并且确实涉及带有包含单个整数表达式的花括号初始化列表的调用,但T 是用户定义的类型, 不是内置类型,表达式也不是常量。
“否”是可以接受的答案,但在这种情况下,请详细说明,请说明没有这样的选项。
【问题讨论】:
-
也许在您的问题中将
void f(std::vector<void *>)更改为void f(std::vector<char>)?这与 void 指针无关。 -
@NeilButterworth 我选择了我能想到的最简单的类型,它不会接受来自
int的初始化。char确实接受来自int的初始化,因此不太能代表真实代码。 -
但正如你所说,
void *确实接受来自 int(或几乎任何东西)的初始化。 -
@NeilButterworth 嗯?不,
void *不接受来自int的初始化。void *p = 1;显然无效。 -
@OZ17 我正在考虑如何在这里工作,但我没有看到它,抱歉。你能详细说明一下吗?
标签: c++ overload-resolution ambiguous-call explicit-constructor