跟我学c++中级篇——c++中的Abominable Function Types

一、Abominable Function Types

Abominable Function Types,令人讨厌(憎恶)的函数类型。这个在c++的技术点中,很少有人了解。那么什么是Abominable Function Types呢?看下面的例子:

using func    = void();
using func_aft = void() const volatile &&;

在上面的两行代码中,声明了两个函数。第一行的代码大家一般都比较熟悉,一般函数都这样声明。但下面一行的代码就比较不一般了,它带了const、volatile和 &&(&)等限定符。要知道,限定符可是在成员函数中才能使用的,它隐式调用了*this,可这里偏偏它又是一个特定的函数类型而不类成员,这就有一个问题了,没有办法指定它的所属类。
换句话说,根本没办法实现了一个这样的函数,只是有一种想象罢了。举个不恰当的例子,“我要去火星”,这个想法没错,但实现不了(至少目前实现不了)。
同时,如果想声明一个此种类型的指针和引用时,会报一个“ill-formed”错误,即下面的代码:

using p = func_aft*;
using r = func_aft&;

这种函数就是一个令人讨厌的函数类型,一般来说,这种类型都具有这个特征,特定的函数带有限定符。
那它有什么用呢?

二、作用

先铺垫一下引用限定符和const。在早期的开发中,如果不想让某个变量被改变,那么可以使用如下的方法来操作:

void Test(const A &a)
{
  //此处无法修改A的成员值
}

在c++11中其实引进了另外一种方式,也就是&,&&,看下面的例子:

#include 

class T {
public:
    T(int n):n_(n){}
    int get()&{
        return this->n_;
    }
private:
    int n_;
};
int main() {
    T t(1);
    std::cout << t.get() << std::endl;          // OK
    //std::cout << std::move(t).get() << std::endl;  // error
    return 0;
}

同理,右值也可以使用上面的代码只是把&改成&&即可。效果也会发生变化,左值就无法使用了,只能使用右值。这时,如果想实现上面早期的方式,可以增加const,注意const一定要在&和&&之前:

#include 

class T {
public:
    T(int n,int n1):n_(n),n1_(n1){}
    int get()const &{
        return this->n_;
    }
    int getr()const &&
    {
      return this->n1_;
    }
private:
    int n_;
    int n1_
};

不过此时需要注意的是,const & 修饰时,此时的值可以是左值也可以是右值,和单纯引用限定时有区别。而const &&时仍然只能是右值。
好,介绍完了背景知识,回到正题。Abominable Function Types有什么作用?
从一个例子开始说明,在c++11中引入了std::function这个对象,那么可以任意声明一个变量如下:

struct T
{
    void operator()() {/* ... */} // not const!
};
const std::function<void()> func(T{});
func(); // OK

在std::function其本身的operator()它是一个常函数,但T的是一个普通函数,这样,一个常量就可以调用一个非常量函数,那么它在多线程执行时,就无法达到要求的不产生况态条件。此时就可以用Abominable Function Types来处理它,即只有拿到的函数类型为const才构建需要的对象。这个在c++23中的move_only_function就是如此做的。

三、实际的应用

在看到上面的分析和说明后,就可以看看它在实际工程中到底有什么作用?先说明一点,它基本上都是在模板元编程用的,其它方向上确实非常非常少。在Traits类型时,这些约束应用到引用和指针时,就需要额外处理这种令人讨厌的函数类型。

template <typename TYPE>
constexpr bool is_function_v = false;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...)> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......)> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile &> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile &&> = true;

template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile &&> = true;

*代码摘自Alisdair Meredith, [email protected],论文《Abominable Function Types》
上面的代码用来处理各种情况下的函数类型的判断,可以测试所有能想到的函数类型保证模板的安全。
下面再看一个可能在实际中遇到的情况:

template 
struct remove_member_pointer { using type = T; };

template 
struct remove_member_pointer { using type = T; };
struct T {
	void test() { std::cout << "abc" << std::endl; };
};
using mfType = typename remove_member_pointer::type;

看一下c++的STL中remove_pointer的可能实现:

template< class T > struct remove_pointer                    {typedef T type;};
template< class T > struct remove_pointer<T*>                {typedef T type;};
template< class T > struct remove_pointer<T* const>          {typedef T type;};
template< class T > struct remove_pointer<T* volatile>       {typedef T type;};
template< class T > struct remove_pointer<T* const volatile> {typedef T type;};

在上面的代码去除上一层的T*是非常容易理解的,而去除函数指针中的classType可能有点不好理解,classType::*可以代表其任意的成员函数,在实际的remove_member_pointer过程中,只是提供了一个typedef的函数指针,这里如果有不明白的可以看一下函数指针中如何使用typedef,这个在std::add_pointer中也有体现:“Otherwise (if T is a cv- or ref-qualified function type), provides the member typedef type which is the type T.The behavior of a program that adds specializations for std::add_pointer is undefined.”

四、总结

今天分析的这个令人讨厌的函数类型和c++11中的引用限定符,都是比较少使用的,在实际工程中估计绝大多数国内的c++程序员都无法用到。那就了解一下,省得再看国外的框架代码时遇到这样的问题,搞不清楚。
消除这种令人讨厌的函数类型有很多种,最简单的就是禁止它们。但这又无法兼容旧得版本标准,这本身就是一个非常不好体验。那么只能选择完善它,在c++23中就对其进行了处理,比如使用this(前面分析过的Deducing This)来处理一些等效的操作等。
一个知识存在,一定有其存在的意义,当确实遇到需要它的问题时,才会发现确实是有用。

你可能感兴趣的:(C++11,C++,c++)