候选函数不可行:没有已知的从 std::vector<derived> 到 std::vector<base> 的转换

新手上路,请多包涵

首先,标题可能无法反映当前问题,因此请随时更改。假设我有以下课程;

 #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. 在 A 类中定义 typedef std::vector<A<int,std::string>> MyWeirdVector; 并使用 se B::MyWeirdVector c; 而不是 std::vector<B> c;
  2. 在 A 类中将每个打印函数定义为 template <typename U> 并接受类型名 U 作为参数。

这两种解决方案都有其自身的缺点。首先,我必须将 c 实例化为 B::MyWeirdVector,其次,我(感觉)没有类型安全性。即使我没有在 <> 中定义类型,第二种解决方案也有效。

那么,是否有一个优雅的解决方案来解决这个问题,比如让隐式类型转换从 std::vector<B>std::vector<A<int,std::string>>

- 编辑 -

感谢@max66 和@Caleth 以及其他伙伴。我只想分享完整的工作示例。请注意,如果您不想发疯,@max66 的答案在 print void 。 (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);
}

原文由 Sezen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
1 个回答

关于什么

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>

- 编辑 -

OP问

如何使用 std::enable_if 使模板打印功能仅对从 A 派生的对象进行操作?

有很多方法;例如,请参阅 Caleth 的答案,其中包含其他模板类型和 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); }

原文由 max66 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题