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

candidate function not viable: no known conversion from std::vector<derived> to std::vector<base>

本文关键字:vector std gt lt base 转换 不可行 候选函数 derived      更新时间:2023-10-16

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

#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 中使用 seB::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以及其他研究员。我只想分享完整的工作示例。请注意,如果您不想发疯,print之前没有void@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);
}

怎么样

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

将每个打印函数定义为模板<typename U>

取而代之的是,请仅定义使用类型名引发错误的打印函数。

由于这两种类型完全不同,因此隐式转换不是一种选择,但我的建议是。

对于最大的通用性:

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>容器的内容,则模板实例化将失败。

相关文章: