функция-кандидат недопустима: нет известного преобразования из std :: vector <derived>в std :: vector <base> - PullRequest
0 голосов
/ 31 мая 2018

Во-первых, заголовок, возможно, не отражает текущий вопрос, поэтому, пожалуйста, не стесняйтесь менять.Предполагая, что у меня есть следующие классы:

#include <iostream>
#include <vector>

template <typename K, class V>
class A {
public:
  K x;
  V y;
  A(K x, V y):x(x), y(y) {}
  void print(A<K, V>& z) {
    std::cout << x + z.x << "-" << y + z.y << std::endl;
  }
  void print(std::vector<A<K,V>> z) {
    for(auto& i:z) {
      print(i);
    }
  }
};

class B:public A<int, std::string> {
public:
  B():A(0, "zero") {}
  B(int x, std::string y):A(x, y) {}
};

void test() {
  B b1(1, "one");
  B b2(2, "two");
  B b3(3, "three");
  B b4(4, "four");
  B b5(5, "five");
  b5.print(b1);
  //
  std::vector<B> c;
  c.push_back(b1);
  c.push_back(b2);
  c.push_back(b3);
  c.push_back(b4);
  b5.print(c);
}

Я получаю следующую ошибку в последней последней строке (b5.print(c));

test_class.cpp:40:6: error: no matching member function for call to 'print'
  b5.print(c);
  ~~~^~~~~
test_class.cpp:10:8: note: candidate function not viable: no known conversion from 'std::vector<B>' to 'A<int, std::__1::basic_string<char> > &' for 1st argument
  void print(A<K, V>& z) {
       ^
test_class.cpp:13:8: note: candidate function not viable: no known conversion from 'vector<B>' to 'vector<A<int, std::__1::basic_string<char> >>' for 1st argument
  void print(std::vector<A<K,V>> z) {
       ^
1 error generated.

Я в основном ожидаю неявное преобразование из vector<B>до std::vector<A<int,std::string>> но это не так.Поэтому я нашел два решения этой проблемы:

  1. Определить typedef std::vector<A<int,std::string>> MyWeirdVector; в классе A и использовать se B::MyWeirdVector c; вместо std::vector<B> c;.
  2. Определить каждую функцию печатикак template <typename U> в классе A и принять имя типа U в качестве аргумента.

У обоих решений есть свой недостаток.Во-первых, я должен создать экземпляр c как B :: MyWeirdVector, а во-вторых, я не чувствую безопасности типов.Второе решение работает, даже если я не определяю тип в <>.

Итак, есть ли элегантное решение этой проблемы, например, неявное преобразование типов из std::vector<B> в std::vector<A<int,std::string>>?

- РЕДАКТИРОВАТЬ -

Благодаря @ max66, @Caleth и другим сотрудникам.Я просто хочу поделиться полным рабочим примером.Пожалуйста, обратите внимание, что void до print не существует для ответа @ max66, если вы не хотите сходить с ума.(1. Все аргументы функции печати: const, 2. Объедините ответы от @ max66 и @Caleth.)

#include <iostream>
#include <vector>
#include <type_traits>

template <typename K, class V>
class A {
public:
  K x;
  V y;
  A(K x, V y):x(x), y(y) {}
  void print(const A<K, V>& z) {
    std::cout << x + z.x << "-" << y + z.y << std::endl;
  }

  // for C++11, thanks to @Caleth
  // template <typename Container, typename = typename std::enable_if<!std::is_base_of< A<K,V>, typename std::remove_reference<Container>::type >::value>::type>
  // void print(Container&& z) {
  //   for(auto& i:z) {
  //     print(i);
  //   }
  // }

  // thanks to @max66
  template <typename T>
  typename std::enable_if<std::is_base_of<A<K, V>, T>::value>::type
    print(std::vector<T> const & z) {
      for(auto const & i:z) print(i);
    }    
  };

class B:public A<int, std::string> {
public:
  B():A(0, "zero") {}
  B(int x, std::string y):A(x, y) {}
};

void test() {
  B b1(1, "one");
  B b2(2, "two");
  B b3(3, "three");
  B b4(4, "four");
  B b5(5, "five");
  b5.print(b1);
  //
  std::vector<B> c;
  c.push_back(b1);
  c.push_back(b2);
  c.push_back(b3);
  c.push_back(b4);
  b5.print(c);
}

Ответы [ 3 ]

0 голосов
/ 31 мая 2018

Определите каждую функцию печати как шаблон <typename U>

Вместо этого определите только функцию печати, которая выдает ошибку с именем типа.

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

0 голосов
/ 31 мая 2018

Для максимальной универсальности:

template<typename Container, typename = std::enable_if_t<!std::is_base_of_v<A<K, V>, std::remove_reference_t<Container>>>>
void print(Container&& z) {
  for(auto & i : z) {
    print(i);
  }
}

Этот является типом .Если вы попытаетесь передать что-то, что не является (потенциально вложенным) контейнером A<K, V>, создание экземпляра шаблона завершится неудачей.

0 голосов
/ 31 мая 2018

Как насчет

template <typename T>
void print(std::vector<T> const & z) {
  for(auto const & i:z) {
    print(i);
  }
}

вместо

void print(std::vector<A<K,V>> z) {
  for(auto& i:z) {
    print(i);
  }
}

?

Я имею в виду: вы не можете иметь неявное преобразование из std::vector<B> в std::vector<A<K, T>>, новы можете управлять содержимым универсального std::vector<T> (универсального T) и получать (в случае) неявное преобразование из T элементов в A<K, T> (если T является производным типом).

Если хотите, вы можете добавить std::enable_if, чтобы включить функцию печати шаблона, только если T получено из A<K, T>.

- EDIT -

ОП спросил

Как я могу использовать std::enable_if, чтобы включить функцию печати шаблона только для объектов, полученных из A?

Есть много способов;см., например, ответ Калета с дополнительными типами шаблонов и std::enable_if для его активации.

Но я предпочитаю возвращаемое значение, активированное std::enable_if.

Что-то как (осторожно: код не проверен)

template <typename T>
typename std::enable_if<std::is_base_of<A<K, V>, T>::value>::type
   print(std::vector<T> const & z)
 { for(auto const & i:z) print(i); }

Если вы можете использовать C ++ 14, вы можете немного упростить (используя std::enable_if_t<> вместо typename std::enable_if<>::type)

template <typename T>
std::enable_if_t<std::is_base_of<A<K, V>, T>::value>
   print(std::vector<T> const & z)
 { for(auto const & i:z) print(i); }

и используя C++ 17 немного больше (std::is_base_of_v<> вместо `std :: is_base_of <> :: value)

template <typename T>
std::enable_if_t<std::is_base_of_v<A<K, V>, T>>
   print(std::vector<T> const & z)
 { for(auto const & i:z) print(i); }
...