Шаблон C ++: нет подходящей функции для вызова - PullRequest
0 голосов
/ 27 июня 2018

Я не могу понять, почему эта программа не может скомпилироваться как с g ++ 7.3, так и clang ++ 5.0 с использованием -std = c ++ 14.

A можно инициализировать с const int, как показано. Постоянная ссылка на A также может быть создана из const int, но вызов f(const A &) с const int завершается неудачно. Почему?

#include <iostream>

struct V {
  int i;
  template <class T>
  V(const T &t) : i{t} {}
};

struct A {
  int i;
  A(V v) : i{v.i} {}
};

void f(const A &) {}

int main() {
  const auto i = 42;
  const A a1{i};              // OK
  std::cout << a1.i << '\n';  // 42
  const A &a2 = A{i};         // OK
  std::cout << a2.i << '\n';  // 42
  f(i);                       // no matching function for call to 'f'
  return 0;
}

Ответы [ 4 ]

0 голосов
/ 27 июня 2018

Два пользовательских преобразования не поддерживаются при инициализации копирования. Простой способ обойти проблему - заключить меня в букву А и перейти к функции f с f(A(i))

0 голосов
/ 27 июня 2018

С учетом f(i);, применяется копия инициализации . И i (с типом const int) необходимо преобразовать в A, требуется два пользовательских преобразования; от const int до V и от V до A. Но в одной неявной последовательности преобразования допускается только одно пользовательское преобразование.

Bot const A a1{i}; и const A &a2 = A{i}; являются прямой инициализацией , только одно неявное преобразование из i (с типом const int) в аргумент конструктора A (т.е. * 1020) *) требуется, поэтому они работают нормально.

Обратите внимание на разницу между инициализацией копирования и прямой инициализацией,

Кроме того, неявное преобразование при инициализации копирования должно производить T непосредственно из инициализатора, в то время как, например, прямая инициализация предполагает неявное преобразование инициализатора в аргумент конструктора Т.

В качестве обходного пути вы можете выполнить явное преобразование i, прежде чем передать его f().

0 голосов
/ 27 июня 2018

Здесь необходимо два последовательных неявных преобразования типов, однако C ++ может выполнить одно неявное преобразование для вас. Если вы хотите, чтобы компилятор генерировал для вас правильный типизированный код, используйте template для функции f, как показано ниже.

template <typename T>
void f(const T & x) { std::cout << x << std::endl;}

Причина, по которой вам нужно преобразование двух типов, заключается в наличии только одного конструктора, который принимает тип V в структуре. Если вы хотите избавиться от преобразования двух типов в качестве второго решения, вы можете добавить другой конструктор, который принимает int в качестве параметра, например, следующий

struct A {
    int i;
    A(V v) : i{v.i} {}
    A(int theI) : i{theI} { }
};
0 голосов
/ 27 июня 2018

Преобразование i в A для вызова функции потребует двух определенных пользователем преобразований (int -> V -> A). Стандарт устанавливает жесткий предел одиночного пользовательского преобразования для каждой последовательности неявного преобразования.

То же самое произошло бы, если бы вы попытались связать a2 с i "напрямую". Поэтому вам необходимо выполнить приведение функционального стиля (A{i}) при задании аргумента для f.

...