c++模板类;具有任意容器类型的函数,如何定义它?
好的,简单的模板问题.假设我定义我的模板类是这样的:
Okay, simple template question. Say I define my template class something like this:
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;
}
问题是关于我的 bar 函数...我需要它能够使用某种标准容器,这就是我包含模板/类型名称 C 部分的原因,以定义该容器类型.但显然这不是正确的做法,因为我的测试班然后抱怨:
The question is about my bar function... I need it to be able to use a standard container of some sort, which is why I included the template/typename C part, to define that container type. But apparently that's not the right way to do it, since my test class then complains that:
错误:bar"未在此范围内声明
error: 'bar' was not declared in this scope
那么我将如何以正确的方式实现我的 bar 功能?也就是说,作为我的模板类的函数,具有任意容器类型......我的模板类的其余部分工作正常(具有不会导致错误的其他函数),只是那个函数有问题.
So how would I go about implementing my bar function the proper way? That is, as a function of my template class, with an arbitrary container type... the rest of my template class works fine (has other functions that don't result in an error), it's just that one function that's problematic.
好的,所以具体的函数(bar)是一个eraseInRange函数,它擦除指定范围内的所有元素:
Okay, so the specific function (bar) is an eraseInRange function, that erases all elements in a specified range:
void eraseInRange(C& container, T const& firstElement, T const& secondElement) {...}
如何使用它的一个例子是:
And an example of how it would be used would be:
eraseInRange(v, 7, 19);
这里的 v 是一个向量.
where v is a vector in this case.
编辑 2:傻我!我应该在我的班级之外声明这个函数,而不是在里面......这是一个非常令人沮丧的错误.无论如何,感谢大家的帮助,虽然问题有点不同,但信息确实帮助我构建了函数,因为在找到我原来的问题后,我确实得到了一些其他令人愉快的错误.所以谢谢你!
EDIT 2: Silly me! I was supposed to declare the function outside of my class, not in it... pretty frustrating mistake to be making. Anyways, thanks everyone for the help, though the problem was a little different, the information did help me construct the function, since after finding my original problem, I did get some other pleasant errors. So thank you!
推荐答案
泛化不超过需要,也不能少.
在某些情况下,该解决方案可能还不够,因为它会匹配具有此类签名的任何模板(例如 shared_ptr
),在这种情况下,您可以使用 type_traits
,非常像 duck-typing (模板一般是duck typed).
In some cases that solution might not be enough as it will match any template with such signature (e.g. shared_ptr
), in which case you could make use of type_traits
, very much like duck-typing (templates are duck typed in general).
#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
}
一个好的模板通常不会人为地限制它们有效的类型类型(为什么要这样做?).但是想象一下在上面的例子中你需要访问一个对象const_iterator
,然后你可以使用SFINAE和type_traits给你的函数设置这些约束.
A good template usually does not artificially restrict the kind of types for which they are valid (why should they?). But imagine in the example above you need to have access to an objects const_iterator
, then you can use SFINAE and type_traits to put those constraints on your function.
泛化不超过需要,也不能少.
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());
}
有关更多此类示例,请查看 <algorithm>
.
For more such examples, look into <algorithm>
.
这种方法的优势在于其简单性,并且基于 ForwardIterator 等概念.它甚至适用于数组.如果您想直接在签名中报告错误,可以将其与特征结合使用.
This approach's strength is its simplicity and is based on concepts like ForwardIterator. It will even work for arrays. If you want to report errors right in the signature, you can combine it with traits.
Kerrek SB 已经近似了最简单的解决方案,尽管它是无效的 C++.更正后的变体如下:
The simplest solution is approximated by Kerrek SB already, though it is invalid C++. The corrected variant goes like so:
#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)
{
//
}
然而:这仅适用于具有两个模板类型参数的容器,因此对于 std::map
将失败(感谢 Luc Danton).
However: this will only work for containers that have exactly two template type arguments, so will fail miserably for std::map
(thanks Luc Danton).
任何辅助参数计数的更正版本如下:
The corrected version for any secondary parameter count is as follows:
#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);
}
但是:对于非模板容器,这仍然会失败(感谢 Luc Danton).
However: this will still fail for non-template containers (thanks Luc Danton).
相关文章