std::is_base_of 用于模板化向量函数参数
std::is_base_of for templated vector function parameter
我想根据给定类型是否是特殊类型的子类,提供 operator>>
的两种不同实现:
class A {};
class B : public A{};
class C {};
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<typename std::enable_if<std::is_base_of<A, T>::value>::type>& vec)
{
std::cout << "Special case called" << std::endl;
return is;
}
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<T>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
void main(int argc, char **argv)
{
std::vector<A> a;
std::vector<B> b;
std::vector<C> c;
std::stringstream ss("A string");
ss >> a;
ss >> b;
ss >> c;
}
打印
General case called
General case called
General case called
将第二个运算符定义更改为
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<typename std::enable_if<!std::is_base_of<A, T>::value>::type>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
不编译因为
error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::stringstream'
所以我可能用错了std::enable_if
。但什么是正确的?这里的模板化 std::vector
有问题吗?
我不认为 std::enable_if 在这里最理想的位置,我会把它放在 return 类型中以启用 SFINAE:
template<typename T>
typename std::enable_if<std::is_base_of<A, T>::value,std::istream>::type&
operator>>(std::istream& is,std::vector<T>& vec)
{
std::cout << "Special case called" << std::endl;
return is;
}
template<typename T>
typename std::enable_if<!std::is_base_of<A, T>::value,std::istream>::type&
operator>>(std::istream& is,std::vector<T>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
@Biggy 的回答正确地演示了一种正确执行此操作的方法。这是我发现更具可读性的替代方案(以及一些小的更正和改进):
template<typename T, typename std::enable_if<std::is_base_of<A, T>{}, int>::type = 0>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
std::cout << "Special case called\n";
return is;
}
template<typename T, typename std::enable_if<!std::is_base_of<A, T>{}, int>::type = 0>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
std::cout << "General case called\n";
return is;
}
或者更好的是,使用标签调度:
template<typename T>
std::istream& impl(std::istream& is, std::vector<T>& vec, std::true_type) {
std::cout << "Special case called\n";
return is;
}
template<typename T>
std::istream& impl(std::istream& is, std::vector<T>& vec, std::false_type) {
std::cout << "General case called\n";
return is;
}
template<typename T>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
return impl(is, vec, std::is_base_of<A, T>{});
}
在实践中,我发现后一种方法在条件数量较多时更易于维护,并且会导致更多 easily-deciphered 错误消息。
至于 为什么你的 代码不起作用,问题是 std::enable_if<std::is_base_of<A, T>::value>::type
作为参数类型不是 T
的可推导上下文,并且没有涉及 T
的其他参数来帮助推论。因此,因为您在调用它时没有明确指定 T
,所以运算符根本不是调用的合格候选者,因为 T
未知。然后,当您将相同的 attempted-SFINAE 放在另一个重载中时, 两个 重载都不可行,您会收到错误消息。一个宽松的经验法则是:如果您需要 typename
(与 enable_if
用法一样),则您不再处于可推导的上下文中。
将 enable_if
放入 return 类型或作为默认模板参数反而有效,因为参数类型(特别是漂亮和简单的 std::vector<T>&
)是这样的 T
是可推导的。
我想根据给定类型是否是特殊类型的子类,提供 operator>>
的两种不同实现:
class A {};
class B : public A{};
class C {};
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<typename std::enable_if<std::is_base_of<A, T>::value>::type>& vec)
{
std::cout << "Special case called" << std::endl;
return is;
}
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<T>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
void main(int argc, char **argv)
{
std::vector<A> a;
std::vector<B> b;
std::vector<C> c;
std::stringstream ss("A string");
ss >> a;
ss >> b;
ss >> c;
}
打印
General case called
General case called
General case called
将第二个运算符定义更改为
template<typename T>
std::istream& operator>>(std::istream& is,
std::vector<typename std::enable_if<!std::is_base_of<A, T>::value>::type>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
不编译因为
error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::stringstream'
所以我可能用错了std::enable_if
。但什么是正确的?这里的模板化 std::vector
有问题吗?
我不认为 std::enable_if 在这里最理想的位置,我会把它放在 return 类型中以启用 SFINAE:
template<typename T>
typename std::enable_if<std::is_base_of<A, T>::value,std::istream>::type&
operator>>(std::istream& is,std::vector<T>& vec)
{
std::cout << "Special case called" << std::endl;
return is;
}
template<typename T>
typename std::enable_if<!std::is_base_of<A, T>::value,std::istream>::type&
operator>>(std::istream& is,std::vector<T>& vec)
{
std::cout << "General case called" << std::endl;
return is;
}
@Biggy 的回答正确地演示了一种正确执行此操作的方法。这是我发现更具可读性的替代方案(以及一些小的更正和改进):
template<typename T, typename std::enable_if<std::is_base_of<A, T>{}, int>::type = 0>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
std::cout << "Special case called\n";
return is;
}
template<typename T, typename std::enable_if<!std::is_base_of<A, T>{}, int>::type = 0>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
std::cout << "General case called\n";
return is;
}
或者更好的是,使用标签调度:
template<typename T>
std::istream& impl(std::istream& is, std::vector<T>& vec, std::true_type) {
std::cout << "Special case called\n";
return is;
}
template<typename T>
std::istream& impl(std::istream& is, std::vector<T>& vec, std::false_type) {
std::cout << "General case called\n";
return is;
}
template<typename T>
std::istream& operator >>(std::istream& is, std::vector<T>& vec) {
return impl(is, vec, std::is_base_of<A, T>{});
}
在实践中,我发现后一种方法在条件数量较多时更易于维护,并且会导致更多 easily-deciphered 错误消息。
至于 为什么你的 代码不起作用,问题是 std::enable_if<std::is_base_of<A, T>::value>::type
作为参数类型不是 T
的可推导上下文,并且没有涉及 T
的其他参数来帮助推论。因此,因为您在调用它时没有明确指定 T
,所以运算符根本不是调用的合格候选者,因为 T
未知。然后,当您将相同的 attempted-SFINAE 放在另一个重载中时, 两个 重载都不可行,您会收到错误消息。一个宽松的经验法则是:如果您需要 typename
(与 enable_if
用法一样),则您不再处于可推导的上下文中。
将 enable_if
放入 return 类型或作为默认模板参数反而有效,因为参数类型(特别是漂亮和简单的 std::vector<T>&
)是这样的 T
是可推导的。