我最近发现C ++中存在.*
运算符(以及密切相关的->*
运算符)。 (参见this问题。)
起初看起来很整洁,但为什么我会需要这样的东西呢?链接问题中的两个答案提供了可以从直接函数调用中受益的人为例子。
在直接函数调用不方便的情况下,可以使用函数对象,例如可以在std::sort
中使用的lambda函数。这消除了间接级别,因此比使用.*
更高效。
链接的问题还提到了此示例的简化版本:
struct A {
int a;
int b;
};
void set_member(A& obj, int A::* ptr, int val){
obj.*ptr = val;
}
int main()
{
A obj;
set_member(obj, &A::b, 5);
set_member(obj, &A::a, 7);
// Both members of obj are now assigned
}
但这是非常微不足道的(可能更好的做法,因为它更清洁,并且不会被A
的成员限制)而不是这样做:
struct A {
int a;
int b;
};
void set_me(int& out, int val){
out = val;
}
int main()
{
A obj;
set_me(obj.b, 5);
set_me(obj.a, 7);
// Both members of obj are now assigned
}
总之,指向成员函数的指针可能被函数对象替换,指向成员指针的变量可能被所述变量或函数对象的直接引用所取代。这样做也可能因为少一个间接而提高代码的效率。
This问题只提供了我的结论所在的示例,因此它没有回答我的问题。
除了使用.*
(其中根本没有选择)的遗留代码的接口之外,何时,真的,我是否想要使用.*
?< / p>
答案 0 :(得分:5)
您可以创建指向成员的指针集合并迭代它们。 E.g:
struct UserStrings
{
std::string first_name;
std::string surname;
std::string preferred_name;
std::string address;
};
...
std::array<std::string UserStrings::*, 4> str_cols = { &UserStrings::first_name, &UserStrings::surname, &UserStrings::preferred_name, &UserStrings::address };
std::vector<UserStrings> users = GetUserStrings();
for (auto& user : users)
{
for (auto& column : str_cols)
{
SanitizeForSQLQuery(user.*column);
}
}
答案 1 :(得分:5)
你的例子太琐碎,无法说明。考虑一点复杂的
struct A {
int a;
int b;
};
void set_n_members(A objs[], unsigned n, int A::* ptr, int val)
{
for (unsigned i = 0; i < n; ++i)
objs[i].*ptr = val;
}
int main()
{
A objs[100];
set_n_members(objs, 100, &A::b, 5);
set_n_members(objs, 100, &A::a, 7);
}
如果没有int A::* ptr
并且不会导致代码膨胀,你会如何重写?
答案 2 :(得分:2)
它用于实现std::mem_fn
,用于实现std::function
。
以下代码显示->*
运算符如何在幼稚的Function
类实现中工作。
同样,您可以使用.*
运算符和类引用来实现成员调用程序类。
#include <iostream>
class A
{
public:
void greet()
{
std::cout << "Hello world"<<std::endl;
}
};
template<typename R, typename ...TArgs>
class Invoker
{
public:
virtual R apply(TArgs&& ...args) = 0;
};
template<typename C, typename R, typename ...TArgs>
class MemberInvoker :public Invoker<R, TArgs...>
{
protected:
C* sender;
R(C::*function)(TArgs ...args);
public:
MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args))
:sender(_sender)
, function(_function)
{
}
virtual R apply(TArgs&& ...args) override
{
return (sender->*function)(std::forward<TArgs>(args)...);
}
};
template<typename T>
class Func
{
};
template<typename R, typename ...TArgs>
class Func<R(TArgs...)>
{
public:
Invoker<R,TArgs...>* invoker=nullptr;
template<typename C>
Func(C* sender, R(C::*function)(TArgs...))
{
invoker =new MemberInvoker<C, R, TArgs...>(sender, function);
}
R operator()(TArgs&& ...args)
{
return invoker->apply(std::forward<TArgs>(args)...);
}
~Func()
{
if (invoker)
{
delete invoker;
invoker = nullptr;
}
}
};
int main()
{
A a;
Func<void()> greetFunc(&a, &A::greet);
greetFunc();
system("PAUSE");
}
答案 3 :(得分:0)
让我们假设您想为C ++编写一个可以使用的LINQ样式库:
struct Person
{
std::string first_name;
std::string last_name;
std::string occupation;
int age;
int children;
};
std::vector<Person> people = loadPeople();
std::vector<std::string> result = from(people)
.where(&Person::last_name == "Smith")
.where(&Person::age > 30)
.select("%s %s",&Person::first_name,&Person::last_name);
for(std::string person : result) { ... };
在封面下,where
函数接受包含指向成员的指针(以及其他内容)的表达式树,并应用于每个向量项以查找匹配的对象。 select
语句接受格式字符串和指向成员的指针,并对sprintf
语句中的任何向量项进行where
样式格式化。
我写过类似的东西,而且还有其他一些东西略有不同(Is there a LINQ library for C++?)。指向成员的指针允许库用户指定他们想要的结构中的任何成员,并且库不需要知道他们可能做什么。