我正在研究C ++中的一个问题,该问题涉及大量数据的大量子集和转换操作。为此,我创建了一个map函数和list comprehensions之类的东西。我发现我写的一堆谓词也有反转,所以我需要写:
template <typename type_t>
bool HasTenFoo(const type_t &t) {
return t.foo >= 10.0;
}
和
template <typename type_t>
bool DoesntHaveTenFoo(const type_t &t) {
return t.foo < 10.0;
}
这些都不是一个真实的例子,但它们具有代表性。我也使用了相当数量的仿函数:
class HasEnoughFoo {
public:
HasEnoughFoo (double bar) { this->bar = bar; }
template<typename type_t>
bool operator()(const type_t &t) const { return t.foo >= bar; }
private:
double bar;
};
其中一些也应该反转。我不想不必要地重复代码,而是编写一个将谓词作为参数并将返回该谓词的(反转值)的仿函数。我的拳头在下面切了一下:
/* -- Returns the opposite of some other predicate -------------------------- */
template<typename predicate_t>
class Not {
public:
template <typename predicate_t>
Not(predicate_t *p) { predicate = p; }
template <typename type_t>
bool operator()(const type_t &t) const {
return !(*predicate)(t);
}
private:
predicate_t *predicate;
};
我会用以下的东西来称呼它:
new_list = old_list.subset(Not<HasEnoughFoo>(&HasEnoughFoo(10.0));
或
new_list = old_list.subset(Not<HasTenFoo>(&HasTenFoo));
当predicate_t
是HasEnoughFoo
这样的仿函数时似乎运行良好,但predicate_t
引用HasTenFoo
这样的常规函数时失败。
Visual Studio抱怨'HasTenFoo' is not a valid template type argument for parameter 'predicate_t'
。有没有办法编写一个可以与仿函数和函数一起使用的Not()谓词,或者我注定要编写几十个谓词及其反转?
答案 0 :(得分:5)
以下是您的代码工作示例(我删除了foo
成员,因此只使用双打)。
template <typename type_t>
bool HasTenFoo(const type_t &t) {
return t >= 10.0;
}
class HasEnoughFoo {
public:
HasEnoughFoo (double bar) { this->bar = bar; }
template<typename type_t>
bool operator()(const type_t &t) const { return t >= bar; }
private:
double bar;
};
template<typename predicate_t>
class Not {
public:
Not(predicate_t p): predicate(p) { }
template <typename type_t>
bool operator()(const type_t &t) const {
return !predicate(t);
}
private:
predicate_t predicate;
};
template <class predicate_type>
Not<predicate_type> Negate(predicate_type p)
{
return p;
}
#include <iostream>
int main()
{
std::cout << Negate(HasTenFoo<double>)(11.0) << '\n';
std::cout << Negate(HasEnoughFoo(13.0))(11.0) << '\n';
}
一些重要的注释:
Not的构造函数使用初始化列表。这消除了谓词类型具有默认构造函数(HasEnoughFoo没有)的要求。
你绝对不想搞乱指向谓词的指针。函数对象应该是轻量级的对象,可以毫无顾虑地复制。
因为Not是一个模板类,具有可能复杂的模板参数,但您通常只是将它用作临时(作为获取谓词的函数的未命名参数),添加一个推导出复杂类型的模板函数为你(在整个标准库中使用的技巧) - 这里Negate
。
答案 1 :(得分:3)
你有两大问题。
第一个问题是HasTenFoo
是模板函数。模板实际上并不存在;你不能取一个地址,因为它不存在。但是,确实存在模板的实例化。 &HasTenFoo
是非法的,&HasTenFoo<Bar>
是合法的。 HasTenFoo<Bar>
是指HasTenFoo
模板的特定实例。
第二个问题是Not
类的模板参数必须是您传递它的函数的类型。如果您提供HasTenFoo<Bar>
,则模板参数应为bool(*)(const Bar&)
。
所以,正确的版本将是
Not<bool(*)(const Bar&)>(&HasTenFoo<Bar>)
请注意,要使其适用于函数和仿函数,您必须存储对象/函数的副本,而不是指向它们的指针。这是首选方法;所有带有仿函数的标准库函数都保留了内部副本。
答案 2 :(得分:3)
内置not1谓词。它需要一个谓词并否定它。这适用于任何派生自unary_predicate的谓词。这将消除对手动滚动谓词的需要。
这与ptr_fun的某种组合可能达到你想要的效果。
编辑, 像这样的东西可能会起作用(警告,完全没有测试,甚至不确定这会编译)
int factorial (int x) {
....
}
std::transform (d.begin (), d.end (), v.begin (), std::ptr_fun (factorial));
std::transform (d.begin (), d.end (), v.begin (), not1(std::ptr_fun (factorial)));
答案 3 :(得分:0)
您可以使用boost::lambda。它允许您使用少量代码构造谓词和其他函子。
答案 4 :(得分:0)
你做错了。函数对象惯用法接受函数指针或函数对象。另外,您不正确地获取了模板函数的地址。您没有指定类型。此外,据我所知,实例化像operator()这样的模板运算符需要显式的.operator()语法,大多数人都不会使用。此外,您生成的函数对象错误。让我告诉你。
template<typename type_t> class HasEnoughFoo {
public:
HasEnoughFoo (double bar) { this->bar = bar; }
bool operator()(const type_t &t) const { return t.foo >= bar; }
private:
double bar;
};
template<typename predicate_t, typename type_t>
class Not {
public:
Not(const predicate_t& p) { predicate = p; }
bool operator()(const type_t &t) const {
return !(predicate(t));
}
private:
predicate_t predicate;
};
现在这个functionoid可以接受接受正确参数的函数指针或函数对象。
typedef type_t MyCustomType;
new_list = old_list.subset(Not<HasEnoughFoo<type_t>>(&HasEnoughFoo<type_t>(10.0));
new_list = old_list.subset(Not<decltype(HasTenFoo<&type_t)>>(&HasTenFoo<type_t>));
本质:
将模板运算符放在函数类的类型上,而不是放在运算符或构造函数上
函数必须有完整的模板参数才能获取地址 - 编译器如何知道你想要的函数地址?
请原谅我使用decltype,我不能打扰输入函数签名。在此插入实际签名。
答案 5 :(得分:0)
函数是仿函数,这意味着如果您正确执行所有操作,Not
模板应该按原样运行。
首先,您的Not
模板声明中存在明显错误 - 构造函数周围有一些奇怪的额外template
。这就是它应该是什么样子
template<typename predicate_t>
class Not {
public:
Not(predicate_t *p) { predicate = p; }
template <typename type_t>
bool operator()(const type_t &t) const {
return !(*predicate)(t);
}
private:
predicate_t *predicate;
};
那个额外的template
正在那里做什么,我不知道。如果您正在尝试创建模板转换构造函数,那么您做错了。也许你发布的不是真正的代码。
现在,要与HasTenFoo
一起使用,我们只需按照以下步骤进行操作
new_list = old_list.subset(Not<bool(const LIST_ELEMENT&)>(&HasTenFoo));
或者,使其更具可读性
typedef bool OrdinaryFuncPredicate(const LIST_ELEMENT&);
new_list = old_list.subset(Not<OrdinaryFuncPredicate>(&HasTenFoo));
请注意用作Not
模板的模板参数的类型。您在示例中使用的内容没有意义(它是一个值,而不是一个类型,这正是编译器明确告诉您的)。由于您使用函数作为谓词,因此必须将函数类型指定为模板参数。此函数类型应将list元素类型作为函数参数。从原始帖子中不清楚列表元素类型是什么,所以我只使用LIST_ELEMENT
名称作为替代。