探讨消除C++可憎函数类型的方案:为何未纳入标准?
关于C++"可憎函数类型"的移除方案探讨
近期接触到C++中的所谓**"abominable function type(可憎函数类型)"**,个人认为如果能将其从语言中完全移除,语法会更简洁。这类问题大多源于成员函数的处理:当获取带const限定的成员函数类型时,去除指向成员的指针部分后得到的就是可憎函数,示例代码如下:
template <typename T> struct remove_ptr_to_member { using type = T; }; template <typename T, typename class_t> struct remove_ptr_to_member<T class_t::*> { using type = T; }; struct container { void func() const; }; using member_function_type = typename remove_ptr_to_member<decltype(container::func)>::type;
我提出的解决方案是:把隐式的this参数纳入成员函数类型,这样原本导致可憎函数的限定符(比如const、&&等)就会直接作用于this参数,此时去除指向成员的指针部分后只会剩下常规函数类型,能大幅减少is_function实现的样板代码。
传统is_function实现方式
// primary template template<class> struct is_function : std::false_type { }; // specialization for regular functions template<class Ret, class... Args> struct is_function<Ret(Args...)> : std::true_type {}; // specialization for variadic functions such as std::printf template<class Ret, class... Args> struct is_function<Ret(Args......)> : std::true_type {}; // specialization for function types that have cv-qualifiers template<class Ret, class... Args> struct is_function<Ret(Args...) const> : std::true_type {}; template<class Ret, class... Args> struct is_function<Ret(Args...) volatile> : std::true_type {}; template<class Ret, class... Args> struct is_function<Ret(Args...) const volatile> : std::true_type {}; // etc... (goes on for a while)
优化后的is_function实现方式
// primary template template <typename> struct is_function : std::false_type { }; // specialization for regular functions template<typename Ret, typename... Args> struct is_function<Ret(Args...)> : std::true_type { }; // specialization for variadic functions such as std::printf template<typename Ret, typename... Args> struct is_function<Ret(Args......)> : std::true_type { }; // same thing but with noexcept template<typename Ret, typename... Args> struct is_function<Ret(Args...) noexcept> : std::true_type { }; template<typename Ret, typename... Args> struct is_function<Ret(Args......) noexcept> : std::true_type { };
该方案是否存在反对意见?
当然有不少核心反对点:
- 兼容性灾难:现有大量代码依赖当前成员函数类型的处理逻辑,比如通过指针-to-member获取带cv限定符的函数类型特性,修改后会直接导致这类代码编译失败,而向后兼容是C++标准制定的核心原则之一。
- 语法直观性降低:把
this参数显式纳入函数类型后,成员函数的类型写法会变得冗长,比如原本的void() const会变成void(container const*),对初学者来说,理解成员函数类型的门槛会更高。 - 类型系统边界模糊:当前C++明确区分成员函数与普通函数的本质差异,成员函数的cv/ref限定符是其特有属性。修改后会把这些限定符转移到
this参数上,抹平两者边界,在模板推导、类型匹配等场景中会引发意想不到的行为。
是否存在未覆盖的场景?
是的,仍有不少场景没被完全覆盖:
- 复合限定符的成员函数:比如
void() const &、void() volatile &&这类同时带cv和引用限定的成员函数,纳入this参数后会变成void(container const&)、void(container volatile&&),虽然理论上属于普通函数类型能被模板覆盖,但实际模板推导的正确性需要额外验证。 - 虚函数重载决议:虚函数的调用依赖成员函数的cv/ref限定符匹配对象的属性,把这些限定符转移到
this参数后,重载决议的逻辑需要重新调整,否则可能出现调用匹配错误。 - 成员函数指针转换规则:现有规则中,非const成员函数指针可以转换为const成员函数指针,但修改后这种转换会变成普通函数指针的转换(比如
void(container*)转void(container const*)),这在C++中是不允许的,会直接打破现有转换规则。
为何未被纳入C++标准?
核心原因集中在以下几点:
- 兼容性优先级远高于语法优化:C++标准委员会对向后兼容的重视程度极高,任何可能大范围破坏现有代码的改动都会被否决。这个方案会彻底改变成员函数类型的本质,影响范围覆盖所有使用指针-to-member函数类型的代码,成本过高。
- 收益与成本不对等:虽然能减少
is_function这类标准库模板的样板代码,但这类代码属于标准库实现细节,普通开发者几乎不需要自己编写。为了优化标准库实现复杂度,付出破坏兼容性的代价完全不值得。 - 现有替代方案已足够:C++11之后引入的
std::invoke、std::function等工具,加上现有的类型萃取模板,已经能很好地处理成员函数的调用和类型转换问题,不需要彻底修改类型系统。 - 不符合语言设计理念:C++的类型系统倾向于保留成员函数的特有属性,将其cv/ref限定符转化为
this参数的限定符,会抹平成员函数与普通函数的本质差异,违背语言的设计初衷。
内容的提问来源于stack exchange,提问作者Nik Tedig
相关产品推荐
相关产品推荐

