如何使用 SFINAE 从 end() 方法到 return (const_)iterator
how to use SFINAE to return (const_)iterator from end() method
我想创建一个集合包装器,其中 end
方法将从成员公开。集合本身可能是也可能不是 const
所以我无法根据它区分 const_iterator
和 iterator
,但是内部集合(模板)定义了常量。我认为使用 enable_if<is_const<T
会实现这一点,但似乎不会。谢谢你的帮助
#include <cassert>
#include <type_traits>
#include <vector>
#include <algorithm>
template <typename ITEMS>
struct collection {
ITEMS& _items;
collection(ITEMS& items) : _items(items) {
}
auto find(int i) const {
return std::find(_items.begin(), _items.end(), i);
}
typename std::enable_if_t<std::is_const<ITEMS>::value>::const_iterator
end() const {
return _items.end();
}
typename std::enable_if_t<!std::is_const<ITEMS>::value>::iterator
end() const {
return _items.end();
}
};
template <typename ITEMS>
collection<ITEMS>
make_collection(ITEMS& items) {
return collection<ITEMS>(items);
}
int main() {
std::vector<int> ints = {1, 2, 3};
auto col = make_collection(ints);
const auto it = col.find(3);
assert(it != col.end());
const auto cints = ints;
auto ccol = make_collection(cints);
const auto cit = ccol.find(3);
assert(cit != ccol.end());
return 0;
}
编辑:仅使用 auto end() const { return _items.end(); }
可行,但我想了解为什么模板魔术不起作用。
您的代码有 2 个问题。首先,std::enable_if_t
要么无效,要么解析为一个类型。如果您不指定默认为 void 的类型。在您的示例中,您没有指定类型。
第二个问题是成员函数依赖于class模板。模板中的所有代码在实例化时都必须有效。当您使用 cont
容器实例化 collection
时,非常量重载将无法推断出 return 类型。但它仍然是class模板的一部分,如果其中一部分失败编译器不会忽略该部分,但会给出编译错误。
解决这个问题的方法是使end
方法都成为模板方法,并从class 转移模板参数。这样 end
方法在调用之前不会被实例化,如果一个失败,只要另一个有效就可以了。
template <typename ITEMS>
struct collection {
ITEMS& _items;
collection(ITEMS& items) : _items(items) {
}
auto find(int i) const {
return std::find(_items.begin(), _items.end(), i);
}
template <typename T = ITEMS>
typename std::enable_if_t<std::is_const<T>::value, T>::const_iterator
end() const {
return _items.end();
}
template <typename T = ITEMS>
typename std::enable_if_t<!std::is_const<T>::value, T>::iterator
end() const {
return _items.end();
}
};
我想创建一个集合包装器,其中 end
方法将从成员公开。集合本身可能是也可能不是 const
所以我无法根据它区分 const_iterator
和 iterator
,但是内部集合(模板)定义了常量。我认为使用 enable_if<is_const<T
会实现这一点,但似乎不会。谢谢你的帮助
#include <cassert>
#include <type_traits>
#include <vector>
#include <algorithm>
template <typename ITEMS>
struct collection {
ITEMS& _items;
collection(ITEMS& items) : _items(items) {
}
auto find(int i) const {
return std::find(_items.begin(), _items.end(), i);
}
typename std::enable_if_t<std::is_const<ITEMS>::value>::const_iterator
end() const {
return _items.end();
}
typename std::enable_if_t<!std::is_const<ITEMS>::value>::iterator
end() const {
return _items.end();
}
};
template <typename ITEMS>
collection<ITEMS>
make_collection(ITEMS& items) {
return collection<ITEMS>(items);
}
int main() {
std::vector<int> ints = {1, 2, 3};
auto col = make_collection(ints);
const auto it = col.find(3);
assert(it != col.end());
const auto cints = ints;
auto ccol = make_collection(cints);
const auto cit = ccol.find(3);
assert(cit != ccol.end());
return 0;
}
编辑:仅使用 auto end() const { return _items.end(); }
可行,但我想了解为什么模板魔术不起作用。
您的代码有 2 个问题。首先,std::enable_if_t
要么无效,要么解析为一个类型。如果您不指定默认为 void 的类型。在您的示例中,您没有指定类型。
第二个问题是成员函数依赖于class模板。模板中的所有代码在实例化时都必须有效。当您使用 cont
容器实例化 collection
时,非常量重载将无法推断出 return 类型。但它仍然是class模板的一部分,如果其中一部分失败编译器不会忽略该部分,但会给出编译错误。
解决这个问题的方法是使end
方法都成为模板方法,并从class 转移模板参数。这样 end
方法在调用之前不会被实例化,如果一个失败,只要另一个有效就可以了。
template <typename ITEMS>
struct collection {
ITEMS& _items;
collection(ITEMS& items) : _items(items) {
}
auto find(int i) const {
return std::find(_items.begin(), _items.end(), i);
}
template <typename T = ITEMS>
typename std::enable_if_t<std::is_const<T>::value, T>::const_iterator
end() const {
return _items.end();
}
template <typename T = ITEMS>
typename std::enable_if_t<!std::is_const<T>::value, T>::iterator
end() const {
return _items.end();
}
};