调用重载的"<大括号括起来的初始值设定项列表>"对于对来说就足够了是模棱两可的

call of overloaded ‘<brace-enclosed initializer list>’ with suffices for pairs is ambiguous

本文关键字:gt 模棱两可 列表 lt 重载 起来 调用      更新时间:2023-10-16

我无法编译以下代码。

void print_number(long n) {
std::cout << n << std::endl;
}
void print_number(float n) {
std::cout << n << std::endl;
}
void print_pair(std::pair<std::string, long> p) {
std::cout << std::get<1>(p) << std::endl;
}
void print_pair(std::pair<std::string, float> p) {
std::cout << std::get<1>(p) << std::endl;
}
int main() {
print_number(12l);
print_number(3.4f);
print_pair({"long", 12l});
print_pair({"float", 3.4f});
return 0;
}

print_number函数运行良好。但是,编译器抱怨print_pair函数过载:error: call of overloaded ‘print_pair(<brace-enclosed initializer list>)’ is ambiguous.

<brace-enclosed initializer list>就足够了还是std::pair不起作用?如何重载接收std::pair参数的函数?

Tl;博士

为了解决歧义,您可以向编译器提供正确的类型:

print_pair(std::make_pair<std::string, long>("long", 12l));
print_pair(std::make_pair<std::string, float>("float", 3.4f));

print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));

歧义问题源于"long""float"不是std::string,而是const char[]

所以当你尝试使用以下表达式构造std::pair时:std::pair{"long", 12l},你得到的是一个std::pair<const char[5], long>

(浮点数相同;即std::pair<const char[5], float>(。

您的过载print_pair接受std::pair<std::string, long>std::pair<std::string, float>。前面的类型都不匹配,因此编译器必须执行转换。因此,它无法自动扣除您要执行的转换。两者都有效。

例如:

std::pair<const char[5], long>  
|
----------------------------------------------------------------
v                                                              v
std::pair<std::string, long>                          std::pair<std::string, float>

要"证明"问题是std::string(既不是long也不是float(,也可以解决构造一个适当的std::string(而不是char数组(的歧义:

print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));

C++17开始,此样板可以简化为:

using namespace std::string_literals;
print_pair(std::pair{"long"s, 12l});
print_pair(std::pair{"float"s, 3.4f});

对于print_pair({"long", 12l});,有 2 种可能的重载:

  • void print_pair(std::pair<std::string, long>)
  • void print_pair(std::pair<std::string, float>)

{"long", 12l}没有类型,但对std::pair<std::string, long>std::pair<std::string, long>都是有效的初始化。

所以这个电话是模棱两可的。

print_pair(std::pair{"long", 12l});也是模棱两可的
,因为std::pair<const char*, long>同样可以转换为std::pair<std::string, long>std::pair<std::string, long>

你必须称它为:

  • print_pair(std::pair{std::string("long"), 12l});完全匹配。

C++的重载解析逻辑不考虑分段转换,它只考虑给定参数/参数对的整个转换。

通过此调用:

print_pair({"long", 12l});

我们有两个可行的候选人。一个候选人参加std::pair<std::string, long>,另一个候选人参加std::pair<std::string, float>

C++没有做的(以及你可能认为它做的(是分别考虑这些部分 - 并说好吧,"long"std::string在两种情况下是相同的,但12llong12lfloat更好,因此转换为pair<string, long>获胜。如果我们有两个接受两个参数的函数,而不是两个需要pairs 的函数,那么它将是这样工作的:

print_two("long", 12l); // calls print_two(string, long)

但C++不是那样工作的。它所做的是将这些部分放在一起考虑。该大括号初始化列表可用于构建std::pair专业化。两者都是可行的。两者都是用户定义的转换。两者都不比另一个好,因此模棱两可。我们真的没有语言中的机制来使这种分段转换起作用。

你必须要么说出你的意思:

print_pair(std::pair<std::string, long>("long", 12l));

或者采取两个论点。

感谢您的所有回答。我投了全部赞成票,但是,我想做的是将函数称为具有异构<brace-enclosed initializer list>的函数print_pair

我最终做的是:

struct number {
number(long n) {
std::cout << n << std::endl;
}
number(float n) {
std::cout << n << std::endl;
}
};
void print_pair(std::pair<std::string, number> p) {}

这样,可以在没有error: call of overloaded ‘print_pair(<brace-enclosed initializer list>)’ is ambiguous的情况下编译以下函数调用。

print_pair({"long", 12l});
print_pair({"float", 3.4f});

请注意,使用构造函数分别采用longfloat的两个struct,我们将回到相同的歧义问题。