避免在人工模糊的重载函数调用中拼出类型

时间:2018-09-23 19:53:30

标签: c++ overload-resolution ambiguous-call explicit-constructor

最小示例程序:

#include <vector>
void f(std::vector<int>)    {} // #1
void f(std::vector<void *>) {} // #2
int main() { f({ 1 }); }

从直觉上来说,这是一个有效的程序:使用重载#1的调用将是有效的,使用重载#2的调用将格式不正确,因此应选择重载#1。这就是c所做的。

不幸的是,按照标准,这似乎是模棱两可的,因为存在std::vector<void *>的构造函数,可以通过将其隐式转换为int来与size_t一起调用。在重载解析期间应该忽略构造函数为explicit的事实,如果选择了该重载,则程序将很容易变形。 GCC拒绝了电话通话的模棱两可,看来这样做是正确的。

我可以通过拼写类型名称f(std::vector<int>{ 1 });来修改代码,以使GCC接受呼叫。我还可以使用带有默认参数的标签调度,以允许显式指定要使用的重载,同时允许像以前一样接受现有的调用。

这两个都是可以接受的,但是当回到真实代码时,它们很快就会变得很冗长。还有另一种选择可以让我避免拼写完整的类型名称,而是坚持使用当前的重载吗?我想了一会儿{ 1, }可以工作,但是当然不行,int i = { 1, };也是完全有效的,不能用来避免#2。

如果有助于排除某些替代方法,则实际代码确实涉及std::vector<int>std::vector<T>,并且确实涉及到带有大括号的初始化程序列表的调用,该列表包含单个整数表达式,但是T是用户定义的类型,而不是内置类型,并且表达式不是常量值。

“否”是可以接受的答案,但是在这种情况下,请详细说明,请表明没有这种选择。

1 个答案:

答案 0 :(得分:0)

总是很难证明它是负面的,但考虑到引入的可能性

using I=std::vector<int>;

我认为您是在问“有没有一种方法可以避免标准转换为size_type来消除竞争中的其他重载”?

很显然,您无法对int本身做任何事情,即使隐式转换为int也可以进行标准转换。但是我们可以(当然)bring SFINAE to bear

struct A {
  int i;
  template<class T,std::enable_if_t<std::is_same_v<T,int>>* =nullptr>
  operator T() const {return i;}
};

然后您可以写

f({A{1}});

(用户定义的文字会简短一些,但您说的是非常量表达式。)

由您决定是否回答是完全由您决定,但是我很确定没有允许标准转换的使用花括号初始化器的其他方法。