C++类基础(九)

字面值类,成员指针与 bind 交互
● 字面值类:可以构造编译期常量的类型
– 其数据成员需要是字面值类型

#include<iostream>
#include<string>
class Str
{
public:
    Str(int val) //在运行期执行,导致#1处的错误
        : x(val)
    {}
private:
    int x = 3;
};

constexpr int a = 3; //OK,编译期字面值常量

constexpr Str a(3); //#1Error: Constexpr variable cannot have non-literal type 'const Str'
int main()
{
    return 0;
}

– 提供 constexpr / consteval 构造函数 (小心使用 consteval )

#include<iostream>
#include<string>
class Str
{
public:
    constexpr Str(int val) //提供constexpr构造函数
        : x(val)
    {}
private:
    int x = 3;
};

constexpr Str a(3); //OK

int main()
{
    return 0;
}
#include<iostream>
#include<string>
//clang++
class Str
{
public:
    consteval Str(int val) //OK since C++20,consteval修饰一个函数,每次调用该函数都会返回一个编译期常量。编译期/运行期调用该函数就会返回一个编译期/运行期常量,即既可以在编译期使用也可以在运行期使用
        : x(val)
    {}
private:
    int x = 3;
};

constexpr Str a(3);

int main()
{
    return 0;
}
class Str
{
public:
    consteval Str(int val) //Error: Unknown type name 'consteval'
        : x(val)
    {}
private:
    int x = 3;
};

constexpr Str a(3);

int main()
{
    int x;
    Str a(x); //--std=c++2a x86-64 clang 10.0.1 OK
    Str b(x); //--std=c++2a x86-64 clang 11.0.0 Error: Call to consteval function 'Str::Str' is not a constant expression
    Str c(x); //--std=c++2a x86-64 clang 12.0.1 Error: Call to consteval function 'Str::Str' is not a constant expression
    Str d(x); //--std=c++2a x86-64 gcc9.4 Error: 'consteval' does not name a type
    Str e(x); //--std=c++2a x86-64 gcc10.1 Error: the value of 'x' is not usable in a constant expression
    return 0;
}

– 平凡的析构函数

class Str
{
public:
    constexpr Str(int val)
        : x(val)
    {}
    //~Str() = default; //平凡的析构函数,即编译器合成的析构函数(不写此行)
    ~Str() {} //#1即使函数体为空也是用户定义的析构函数的逻辑,运行期销毁对象时调用
private:
    int x = 3;
};

constexpr Str a(3); //编译器无法知道在编译期如何销毁该对象,使用#1处的析构导致编译失败

int main()
{
    return 0;
}

– 提供 constexpr / consteval 成员函数 (小心使用 consteval )

class Str
{
public:
    constexpr Str(int val) //可在编译期调用,也可在运行期调用
        : x(val)
    {}
    Str(double f) //只能在运行期调用
        : x(f)
    {}
    constexpr int funexpr() const
    {
        return x+1;
    }
    consteval int funeval() const
    {
        return x+1;
    }
private:
    int x = 3;
};

constexpr Str a(3);

int main()
{
    Str b(3.0);
    b.funexpr(); //OK
    Str a(5.0);
    a.funeval(); //--std=c++2a x86-64 gcc 10.1 Error
    return a.funexpr(); //OK
}

– 注意:从 C++14 起 constexpr / consteval 成员函数非 const 成员函数

class Str
{
public:
    constexpr Str(int val)
        : x(val)
    {}
    constexpr int fun() //C++11时constexpr函数缺省为const,即constexpr int fun() const,通常constexpr函数体只能包含一条返回语句
        return x+1;
    }
private:
    int x = 3;
};

constexpr Str a(3);

int main()
{
    return a.fun(); //OK, a是const变量
}
class Str
{
public:
    constexpr Str(int val)
        : x(val)
    {}
    constexpr int fun() const //C++14时constexpr函数缺省为非const,所以加入const修饰符
    {
        return x+1;
    }
private:
    int x = 3;
};

int main()
{
    return a.fun(); //Error: 'this' argument to member function 'fun' has type 'const Str', but function is not marked const
}
constexpr int MyFun(int x) //C++14可以引入复杂的逻辑,函数体内的变量i是可以改变的,所以缺省是非const
{
    for(int i=0;i<10;++i)
    {
        x += i;
    }
    return x;
}
class Str //字面值类
{
public:
    constexpr Str(int val)
        : x(val)
    {}
    constexpr int fun() const //C++14时constexpr函数缺省为非const,所以加入const修饰符
    {
        return x+1;
    }
    constexpr void inc()
    {
        x = x +1;
    }
    constexpr int read() const
    {
        return x;
    }
private:
    int x = 3;
};

constexpr int MyFun()
{
    Str x(10);
    x.inc(); //可以通过constexpr void inc()修改字面值对象的中间状态
    x.inc();
    x.inc();
    return x.read();
}
int main()
{
    std::cout << MyFun() << std::endl;
    return 0;
}

在这里插入图片描述

● 成员指针
– 数据成员指针类型示例: int A::;
– 成员函数指针类型示例: int (A::
)(double);

#include<type_traits>
class Str
{
};
class Str2
{
};

int main()
{
    int Str::*ptr; //ptr的类型是int Str::,ptr指向的基本类型是int,ptr位于类Str中
    void (Str::* ptr_fun)(); //ptr_fun的类型是void (Str::) ()。ptr_fun指向的基本类型是一个返回值为void的函数,ptr_fun位于类Str中
    int Str2::*ptr2;
    std::cout << std::is_same_v<decltype(ptr), decltype(ptr2)> <<std::endl; //因为ptr1和ptr2隶属不同的类,所以它俩类型不同
    return 0;
}

在这里插入图片描述

class Str
{
public:
    int x = 10;
};
class Str2
{
public:
    int y = 10;
};
int main()
{
    int Str::*ptr = &Str::x;
    int Str2::*ptr2 = &Str2::y;
    ptr2-ptr; //Error: Invalid operands to binary expression ('int Str2::*' and 'int Str::*')
    return 0;
}

– 成员指针对象赋值: auto ptr = &A::x;

class Str
{
public:
    int x = 10;
};
class Str2
{
public:
    int y = 10;
};
int main()
{
    int Str::*ptr = &Str::x; //using MemberVarPtr_13 = int Str::*; MemberVarPtr_13 ptr = &Str::x;
    int Str2::*ptr2 = &Str2::y; //  using MemberVarPtr_14 = int Str2::*; MemberVarPtr_14 ptr2 = &Str2::y;
    return 0;
}
class Str
{
public:
    int x = 10;
    int y = 10;

    void fun() {};
    void fun(double) {};
};
int main()
{
    int Str::*ptr = &Str::x;
    int Str::*ptr2 = &Str::y;
    void (Str::* ptr_fun) () = &Str::fun; //OK
    auto ptr_fun2= &Str::fun; //Error: Variable 'ptr_fun2' with type 'auto' has incompatible initializer of type '<overloaded function type>'
    return 0;
}
class Str
{
public:
    int x = 10;
    int y = 10;

    void fun() {};
};
int main()
{
    int Str::*ptr = &Str::x;
    int Str::*ptr2 = &Str::y;
    void (Str::* ptr_fun) () = &Str::fun; //OK
    auto ptr_fun2= &Str::fun; //OK
    return 0;
}

● 注意域操作符子表达式不能加小括号(否则 A::x 一定要有意义)

class Str
{
public:
    int x;
    int y;

    void fun() {};
};
int main()
{
    int Str::*ptr = &(Str::x); //Error: Invalid use of non-static data member 'x'
    return 0;
}
class Str
{
public:
    static int x; //静态变量
    int y;

    void fun() {};
};
int main()
{
    //ptr类型是int Str::*, &(Str::x)类型是int*,因为类Str中的x是静态整型,不依赖于类对象,单独占某块内存
    //int Str::*ptr = &(Str::x); //Error: Cannot initialize a variable of type 'int Str::*' with an rvalue of type 'int *'
    int val = Str::x; //OK
    int* ptr2 = &(Str::x); //Error: undefined reference to 'Str::x'
    return 0;
}
class Str
{
public:
    inline static int x; //内联静态
    int y;

    void fun() {};
};
int main()
{
    int* ptr2 = &(Str::x); //OK
    return 0;
}

– 成员指针的使用:
● 对象 .* 成员指针
● 对象指针 ->* 成员指针

class Str
{
public:
    int x;
    int y;

    void fun() {};
};
int main()
{
    int Str::*ptr = &Str::x;
    Str obj;
    obj.x = 3;
    std::cout << obj.*ptr << std::endl;
    Str* ObjPtr = &obj;
    std::cout << ObjPtr->*ptr << std::endl;
    return 0;
}

在这里插入图片描述

● bind 交互
– 使用 bind + 成员指针构造可调用对象

#include<functional>  //std::bind包含于该头文件中
class Str
{
public:
    int x;
    int y;

    void fun(double x)
    {
        std::cout << x <<std::endl;
    };
};
int main()
{
    auto ptr = &Str::fun;
    Str obj;
    (obj.*ptr)(100.0); //OK
    //(*ptr)(100.0); //Error: Indirection requires pointer operand ('void (Str::*)(double)' invalid)

    //auto x = std::bind(ptr, 100.0); //Error: In template: static_assert failed due to requirement 'integral_constant<bool, false>::value ? sizeof...(_BoundArgs) >= integral_constant<unsigned long long, 1>::value + 1 : sizeof...(_BoundArgs) == integral_constant<unsigned long long, 1>::value + 1' "Wrong number of arguments for pointer-to-member"

    auto y = std::bind(ptr, obj, 100); //OK
    y();
    return 0;
}

在这里插入图片描述

– 注意这种方法也可以基于数据成员指针构造可调用对象

class Str
{
public:
    int x;
    int y;

    void fun(double x)
    {
        std::cout << x <<std::endl;
    };
};
int main()
{
    Str obj;
    auto ptr2 = &Str::x;
    obj.*ptr2 = 3;
    auto x = std::bind(ptr2, obj); //OK
    std::cout << x() << std::endl;
    return 0;
}

在这里插入图片描述

参考
深蓝学院:C++基础与深度解析
C++ Insights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值