C++标准能否允许以自由函数形式调用成员函数?是否存在矛盾?
成员函数指针的调用语法扩展提案探讨
问题背景
C++中,成员函数指针必须通过.*(或->*)语法调用,因此无法直接传递给普通高阶函数,例如下方代码会触发编译错误:
#include <vector> void for_each(auto const& v, auto f) { for (auto const& e : v) f(e); // 错误:必须使用 '.*' 或 '->*' 调用成员函数指针,例如 '(... ->* f) (...)' } struct Foo { void bar(); }; int main() { std::vector<Foo> v(10); for_each(v, &Foo::bar); // 尝试传递成员函数指针到高阶函数 }
现有解决方案
C++标准库提供两种成熟的解决思路:
- 用
std::mem_fn转换成员函数指针:将成员函数指针转为类自由函数风格的可调用对象,即可正常传递给高阶函数:
int main() { std::vector<Foo> v(10); for_each(v, std::mem_fn(&Foo::bar)); // 编译通过 }
- 修改高阶函数用
std::invoke统一调用:std::invoke支持直接调用各类可调用对象(包括成员函数指针),标准库的std::for_each已内置该支持:
void for_each(auto const& v, auto f) { for (auto const& e : v) std::invoke(f, e); // 编译通过 }
核心提案:语法层面整合std::mem_fn功能
既然当前语法(&Foo::bar)(Foo{})是无效的,能否在C++标准中将其合法化,使其等价于先调用std::mem_fn(&Foo::bar)再执行调用?这相当于把std::mem_fn的工具能力直接整合到语言语法中。
该提案是否具备可行性?会产生哪些不良副作用?目前来看,由于该语法当前本身无效,似乎不会破坏现有正常代码。
潜在兼容性风险:SFINAE依赖
撰写问题时,我想到一种可能的兼容性问题:部分代码的SFINAE逻辑依赖于该语法的无效性。比如下方代码中,如果允许(&Foo::bar)(Foo{}),第二个static_assert就会失败:
#include <type_traits> #include <vector> struct Foo { void bar(); }; template<typename F, typename = void> struct Trait : public std::false_type {}; template<typename F> struct Trait<F, std::void_t<decltype(std::declval<F>()(std::declval<Foo>()))>> : public std::true_type {}; auto constexpr freeBar = [](Foo){}; int main() { static_assert(Trait<decltype(freeBar)>::value); static_assert(!Trait<decltype(&Foo::bar)>::value); }
不过,此前在自答的评论中有人指出,这不能成为阻止该提案的理由——毕竟如果要完全避免破坏依赖SFINAE检测无效代码的现有代码,几乎无法为C++标准添加任何新特性。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

