【发布时间】:2018-05-31 14:48:38
【问题描述】:
首先,标题可能无法反映当前问题,因此请随时更改。假设我有以下课程;
#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>> 的隐式转换,但事实并非如此。因此,我想出了两个解决方案。
- 定义
typedef std::vector<A<int,std::string>> MyWeirdVector;在 A 类中并使用 seB::MyWeirdVector c;而不是std::vector<B> c;。 - 在 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);
}
【问题讨论】:
-
这是个通病,
std::vector<A>和std::vector<B>是完全不相关的类型,即使A和B是相关的。 -
打印函数不应该是
A的成员(可以是静态方法或独立的可能是友元函数),它应该接受常量引用作为参数。 -
@Slava
print正在使用它被调用的实例的成员,以及它的参数的成员 -
@Sezen 我错过了它使用对象数据,是的,在这种情况下它很好,但它应该是接受 const 引用的 const 方法。
-
注意:如果您的打印向量会以某种方式工作,那么当您按值传递向量时,就会出现切片问题。
标签: c++ c++11 inheritance vector type-conversion