首先,标题可能无法反映当前的问题,所以请随意更改。假设我有以下课程;
#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类中,使用se B::MyWeirdVector c;
代替
std::vector<B> c;
。template <typename U>
,并接受typename U作为参数。两种解决方案都有其自身的缺点。首先,我必须将c实例化为B :: MyWeirdVector,其次,我不(感觉)具有类型安全性。即使我没有在<>
中定义类型,第二个解决方案也能正常工作。
那么,这个问题有一个优雅的解决方案,就是让从std::vector<B>
到std::vector<A<int,std::string>>
的隐式类型转换?
- 编辑 -
感谢@ max66和@Caleth以及其他人。我只想分享完整的工作实例。请注意,如果你不想发疯,那么在{max}的答案之前void
之前没有print
。 (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);
}
答案 0 :(得分:3)
怎么样?
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
)的内容并获取(在case)从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&lt;&gt; :: 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); }
答案 1 :(得分:1)
将每个打印功能定义为模板
<typename U>
而不是这样,只定义使用typename抛出错误的print函数。
由于两种类型完全不同,隐式转换不是一种选择,但我的建议是。
答案 2 :(得分:1)
为了最大限度的通用性:
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>
的(可能是嵌套的)容器,则模板实例化将失败。