字面值类,成员指针与 bind 交互
● 字面值类:可以构造编译期常量的类型
– 其数据成员需要是字面值类型
#include
#include
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
#include
class Str
{
public:constexpr Str(int val) //提供constexpr构造函数: x(val){}
private:int x = 3;
};constexpr Str a(3); //OKint main()
{return 0;
}
#include
#include
//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 OKStr b(x); //--std=c++2a x86-64 clang 11.0.0 Error: Call to consteval function 'Str::Str' is not a constant expressionStr c(x); //--std=c++2a x86-64 clang 12.0.1 Error: Call to consteval function 'Str::Str' is not a constant expressionStr d(x); //--std=c++2a x86-64 gcc9.4 Error: 'consteval' does not name a typeStr e(x); //--std=c++2a x86-64 gcc10.1 Error: the value of 'x' is not usable in a constant expressionreturn 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(); //OKStr a(5.0);a.funeval(); //--std=c++2a x86-64 gcc 10.1 Errorreturn 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
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 <
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; //OKauto ptr_fun2= &Str::fun; //Error: Variable 'ptr_fun2' with type 'auto' has incompatible initializer of 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; //OKauto ptr_fun2= &Str::fun; //OKreturn 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; //OKint* 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); //OKreturn 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 //std::bind包含于该头文件中
class Str
{
public:int x;int y;void fun(double x){std::cout << x <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::value ? sizeof...(_BoundArgs) >= integral_constant::value + 1 : sizeof...(_BoundArgs) == integral_constant::value + 1' "Wrong number of arguments for pointer-to-member"auto y = std::bind(ptr, obj, 100); //OKy();return 0;
}
– 注意这种方法也可以基于数据成员指针构造可调用对象
class Str
{
public:int x;int y;void fun(double x){std::cout << x <Str obj;auto ptr2 = &Str::x;obj.*ptr2 = 3;auto x = std::bind(ptr2, obj); //OKstd::cout << x() << std::endl;return 0;
}
参考
深蓝学院:C++基础与深度解析
C++ Insights