字面值类,成员指针与 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