вызов перегружен с парой достаточно неоднозначно - PullRequest
4 голосов
/ 05 февраля 2020

Я не могу скомпилировать следующий код.

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?

Ответы [ 4 ]

3 голосов
/ 05 февраля 2020

Tl; dr

Чтобы устранить неоднозначность, вы можете предоставить компилятору правильный тип:

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});
2 голосов
/ 05 февраля 2020

C ++ logs разрешения перегрузки c не учитывает пошаговое преобразование, оно учитывает только целое преобразование для данной пары параметр / аргумент.

С помощью этого вызова:

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

У нас есть два жизнеспособных кандидата. Один кандидат получает std::pair<std::string, long>, а другой - std::pair<std::string, float>.

Что C ++ не делает (и что вы, возможно, думаете, что он делает), так это рассматривайте эти части отдельно - и говорите, что хорошо, "long" - std::string одинаковы в обоих случаях, но 12l long лучше 12l float, в результате конверсия в pair<string, long> выигрывает. Это будет таким, как оно будет работать, если вместо двух функций, которые принимают pair с, у нас есть две функции, которые принимают два аргумента:

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

Но C ++ не работает как это. Что он делает, так это рассматривает эти части вместе. Этот braced-init-list может использоваться для построения обеих std::pair специализаций. Оба жизнеспособны. Оба являются определяемыми пользователем преобразованиями. Ни один из них не лучше другого, а значит, неоднозначен. У нас действительно нет механизма в языке, чтобы заставить этот вид кусочного преобразования работать.

Вы должны либо просто сказать, что вы имеете в виду:

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

, либо принять два аргумента.

2 голосов
/ 05 февраля 2020

При 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});, чтобы иметь точное совпадение.
0 голосов
/ 06 февраля 2020

Спасибо за все ответы. Я проголосовал за всех из них, однако, что я хотел сделать, это вызвать функцию print_pair с разнородными <brace-enclosed initializer list> с.

Что я наконец-то сделал:

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});

Обратите внимание, что, используя два struct s, чьи конструкторы принимают long и float соответственно, мы будем go Вернемся к той же проблеме двусмысленности.

...