Fau*_*ult 20 c++ containers templates
好的,简单的模板问题.假设我将模板类定义为:
template<typename T>
class foo {
public:
foo(T const& first, T const& second) : first(first), second(second) {}
template<typename C>
void bar(C& container, T const& baz) {
//...
}
private:
T first;
T second;
}
Run Code Online (Sandbox Code Playgroud)
问题是关于我的bar函数...我需要它能够使用某种标准容器,这就是为什么我包含模板/ typename C部分来定义容器类型.但显然这不是正确的方法,因为我的测试类然后抱怨:
错误:此范围内未声明"bar"
那么我将如何以正确的方式实现我的条形函数呢?也就是说,作为我的模板类的函数,使用任意容器类型...我的模板类的其余部分工作正常(有其他函数不会导致错误),它只是一个有问题的函数.
编辑:好的,所以特定的函数(bar)是eraseInRange函数,它擦除指定范围内的所有元素:
void eraseInRange(C& container, T const& firstElement, T const& secondElement) {...}
Run Code Online (Sandbox Code Playgroud)
它将如何使用的一个例子是:
eraseInRange(v, 7, 19);
Run Code Online (Sandbox Code Playgroud)
其中v是这种情况下的向量.
编辑2:傻我!我本来应该在我班级之外宣布这个功能,而不是在它里面...这是非常令人沮丧的错误.无论如何,感谢大家的帮助,虽然问题有点不同,但信息确实帮助我构建了这个功能,因为在找到原来的问题后,我确实得到了一些其他令人愉快的错误.所以谢谢!
Seb*_*ach 31
概括不超过需要,而不是更少.
在某些情况下,解决方案可能不够,因为它将匹配具有此类签名的任何模板(例如shared_ptr),在这种情况下,您可以使用type_traits,非常类似于鸭子类型(模板通常是鸭子类型).
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template<typename T>
struct has_const_iterator
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// bar() is defined for Containers that define const_iterator as well
// as value_type.
template <typename Container>
typename std::enable_if<has_const_iterator<Container>::value,
void>::type
bar(const Container &c, typename Container::value_type const & t)
{
// Note: no extra check needed for value_type, the check comes for
// free in the function signature already.
}
template <typename T>
class DoesNotHaveConstIterator {};
#include <vector>
int main () {
std::vector<float> c;
bar (c, 1.2f);
DoesNotHaveConstIterator<float> b;
bar (b, 1.2f); // correctly fails to compile
}
Run Code Online (Sandbox Code Playgroud)
一个好的模板通常并不会人为限制的那种类型,他们是有效的(他们为什么要?).但是想象一下,在上面的示例中,您需要访问对象const_iterator,然后您可以使用SFINAE和type_traits将这些约束放在您的函数上.
概括不超过需要,而不是更少.
template <typename Iter>
void bar (Iter it, Iter end) {
for (; it!=end; ++it) { /*...*/ }
}
#include <vector>
int main () {
std::vector<float> c;
bar (c.begin(), c.end());
}
Run Code Online (Sandbox Code Playgroud)
有关更多此类示例,请查看<algorithm>.
这种方法的优势在于它的简单性,并且基于ForwardIterator等概念.它甚至适用于数组.如果要在签名中报告错误,可以将其与特征结合使用.
std带签名的容器std::vector(不推荐)最简单的解决方案已经由Kerrek SB近似,尽管它是无效的C++.修正后的变体如下:
#include <memory> // for std::allocator
template <template <typename, typename> class Container,
typename Value,
typename Allocator=std::allocator<Value> >
void bar(const Container<Value, Allocator> & c, const Value & t)
{
//
}
Run Code Online (Sandbox Code Playgroud)
但是:这只适用于具有两个模板类型参数的容器,因此会失败std::map(感谢Luc Danton).
任何辅助参数计数的更正版本如下:
#include <memory> // for std::allocator<>
template <template <typename, typename...> class Container,
typename Value,
typename... AddParams >
void bar(const Container<Value, AddParams...> & c, const Value & t)
{
//
}
template <typename T>
class OneParameterVector {};
#include <vector>
int main () {
OneParameterVector<float> b;
bar (b, 1.2f);
std::vector<float> c;
bar (c, 1.2f);
}
Run Code Online (Sandbox Code Playgroud)
但是:对于非模板容器,这仍然会失败(感谢Luc Danton).
在C++20中,通过添加Concepts和Ranges库,我们可以简单地解决这个问题std::ranges::common_range:
void printContainer(const std::ranges::common_range auto & container);
{
for(const auto& item : container) std::cout << item;
}
Run Code Online (Sandbox Code Playgroud)
这里,common_range是所有stl容器都满足的一个概念。你可以container通过以下方式获取 的值类型:
std::ranges::range_value_t<decltype(container)>
Run Code Online (Sandbox Code Playgroud)
您还可以创建自己的容器类型,通过定义良好的迭代器类型和函数来满足这一it begin()概念it end()。
std::ranges::range,它的要求比 稍微宽松一些common_range,因此它可以允许更多自定义类型。尝试使用不令人满意的类型调用该函数会给您带来类似 的错误template argument deduction/substitution failed: constraints not satisfied。
在模板模板参数上模板化模板:
template <template <typename, typename...> class Container>
void bar(const Container<T> & c, const T & t)
{
//
}
Run Code Online (Sandbox Code Playgroud)
如果您没有C++ 11,则无法使用可变参数模板,并且必须提供与容器一样多的模板参数.例如,对于序列容器,您可能需要两个:
template <template <typename, typename> class Container, typename Alloc>
void bar(const Container<T, Alloc> & c, const T & t);
Run Code Online (Sandbox Code Playgroud)
或者,如果您只想允许自身为模板实例的分配器:
template <template <typename, typename> class Container, template <typename> class Alloc>
void bar(const Container<T, Alloc<T> > & c, const T & t);
Run Code Online (Sandbox Code Playgroud)
正如我在评论中所建议的那样,我个人更喜欢将整个容器设为模板类型,并使用特征来检查它是否有效.像这样的东西:
template <typename Container>
typename std::enable_if<std::is_same<typename Container::value_type, T>::value, void>::type
bar(const Container & c, const T & t);
Run Code Online (Sandbox Code Playgroud)
这更灵活,因为容器现在可以是暴露value_type成员类型的任何东西.可以设想用于检查成员函数和迭代器的更复杂的特征; 例如,漂亮的打印机实现了其中的一些.