标签:9.4 说明 guard field defs 依赖性 头文件 mpi 删除
class Widget { ... private: int x {0}; // ok int y = 0; // ok int z(0); // error };
std::atomic<int> ai1 {0}; // ok std::atomic<int> ai2 (0); //ok std::atomic<int> ai3 = 0; // error
double x, y, z; ... int sum1 {x+y+z}; // error int sum2 (x+y+z); // ok int sum3 = x+y+z; // ok
Widget w1(); // error Widget w2{}; // ok
class Widget { public: Widget(int i, bool b); Widget(int i, double d); ... }; Widget w1(10, true); // calling 1 Widget w2{10, true}; // calling 2 Widget w3(10, 5.0); // calling 1 Widget w4{10, 5.0}; // calling 2
class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<long double> il); ... }; Widget w1(10, true); // calling 1 Widget w2{10, true}; // calling 3, 10 and true convert to long double Widget w3(10, 5.0); // calling 1 Widget w4{10, 5.0}; // calling 3 , 10 and 5.0 convert to long double
Widget w5(w4); // copy construction Widget w6{w4}; // std::initializer_list construction Widget w7(std::move(w4)); // move construction Widget w8{std::move(w4)}; // std::initializer_list construction
class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<bool> il); ... }; Widget w{10, 5.0}; // error, requires narrowing conversions
class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<std::string> il); ... }; Widget w1(10, true); // calling 1 Widget w2{10, true}; // calling 1 Widget w3(10, 5.0); // calling 2 Widget w4{10, 5.0}; // calling 2
class Widget { public: Widget(); Widget(std::initializer_list<int> il); ... }; Widget w1; // calling 1 Widget w2{}; // calling 1 Widget w3{{}}; // calling 2 Widget w4({}); // calling 2
void f(int); void f(bool); void f(void*); f(0); // calls f(int) f(NULL); // might not compile, but typically calls f(int)
nullptr的优点在于它没有一个整型类型,也没有一个指针类型,但是可以代表所有类型的指针,nullptr的实际类型是nullptr_t,可以被隐式地转换成所有原始指针类型
f(nullptr); // calls f(void*)
当在使用模板时,nullptr的优势就发挥出来了,可以转换成任意指针类型
int f1(std::shared_ptr<Widget> spw); int f2(std::unique_ptr<Widget> upw); bool f3(Widget* pw); std::mutex f1m, f2m, f3m; template<typename FuncType, typename MuxType, typename PtrType> auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr)) { using MuxGuard = std::lock_guard<MuxType>; MuxGuard g(mutex); return func(ptr); } auto result1 = lockAndCall(f1, f1m, 0); // error, PtrType is int auto result2 = lockAndCall(f2, f2m, NULL); // error, PtrType is int / long auto result3 = lockAndCall(f3, f3m, nullptr); // ok
typedef void (*FP)(int, const std::string&); using FP = void(*)(int, const std::string&);
template<typename T> using MyAllocList = std::list<T, MyAlloc<T>>; MyAllocList<Widget> lw; template<typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; }; MyAllocList<Widget>::type lw;
如果要在模板内部创建一个持有模板参数类型的链表,必须在typedef名字前面加上typename
template<typename T> class Widget { private: typename MyAllocList<T>::type list; ... };
MyAllocList<T>::type指的是一个取决于模板类型参数T的类型,因此就是一个依赖类型,C++规定依赖类型前面必须加上typename
如果使用alias定义模板,就不需要typename了
template<typename T> using MyAllocList = std::list<T, MyAlloc<T>>; template<typename T> class Widget { private: MyAllocList<T> list; ... };
此处看起来MyAllocList<T>是一个与模板参数T存在依赖关系的对象,但是当编译器处理Widget模板时,它知道MyAllocList<T>是一个类型的名字,因为MyAllocList是一个别名模板:它必须命名一个类型,因此MyAllocList<T>是一个无依赖类型,也就不需要typename了
在typedef中,当编译器在Widget模板中看到MyAllocList<T>::type时,它们不能确定这是否是一个类型,因为有可能是MyAllocList<T>的一个特例而它们没看到,例如:
class Wine{...}; template<> class MyAllocList<Wine> { private: enum class WineType {White, Red, Rose}; WineType type; //!!!!!!!!!!!!!!! ... };
C++11以类型萃取的形式提供了许多形式转换工具,模板都在<type_traits>头文件中,例如
std::remove_const<T>::type std::remove_reference<T>::type std::add_lvalue_reference<T>::type
但是要在模板内部使用它们时,仍然要在前面加上typename,因为它们实际上还是用嵌套typedef实现的
但在C++14中,它们有了替代的方案
std::remove_const_t<T> std::remove_reference_t<T> std::add_lvalue_reference_t<T>
原理显而易见
template<class T> using remove_const_t = typename remove_const<T>::type; template<class T> using remove_reference_t = typename remove_reference<T>::type; template<class T> using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
enum Color {black, white, red}; auto while = false; // error, while already declared in this scope
enum class Color {black, white, red}; auto white = false; // fine Color c = white; // error, no enumerator named “white" is in this scope Color c = Color::white; // fine auto c = Color::white; // fine
enum Color {black, white, red}; std::vector<std::size_t> primeFactors(std::size_t x); Color c = red; ... if( c < 14.5){ // compare Color to double!! auto factors = primeFactors(c); // compute prime factors of a Color!! ... } enum class Color {black, white, red}; Color c = Color::red; ... if( c < 14.5){ // error, can‘t compare Color and double!!! auto factors = primeFactors(c); // error, can‘t pass Color to function expecting std::size_t ... }
if( static_cast<double>(c) < 14.5 ){ // valid auto factors = primeFactors(static_cast<std::size_t>(c)); // valid ... }
#file 1 enum Status { good = 0, failed = 1, incomplete = 100, corrupt = 200, audited = 500, indeterminate = 0xFFFFFFFF }; #file 2 enum class Status; void continueProcessing(Status s);
enum class Status; //int, declaration enum class Status: std::uint32_t; //uin32_t, declaration enum Color: std::uint8_t;// uint8_t, declaration enum class Status: std::uint32_t { good = 0, failed = 1, incomplete = 100, corrupt = 200, audited = 500, indeterminate = 0xFFFFFFFF };
无范围限制的enum在C++11的std::tuples中的用途
using UserInfo = std::tuple<std::string, std::string, std::size_t>; // name, email, reputation UserInfo uInfo; ... auto val = std::get<1>(uInfo); // get value of field 1, but can you always remember what the hell 1 represents? //Improve enum UserInfoFields {uiName, uiEmail, uiReputation}; UserInfo uInfo; ... auto val = std::get<uiEmail>(uInfo); // implicit conversion from UserInfoFields to std::size_t, which is the type that std::get requires
如果要改写成有范围限制的enum,略显拖沓
enum class UserInfoFields {uiName, uiEmail, uiReputation}; UserInfo uInfo; ... auto val = std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>(uInfo);
bool isLucky(int number); bool isLucky(char) = delete; bool isLucky(bool) = delete; bool isLucky(double) = delete;
虽然删除的函数不能使用,但仍然是程序的一部分,因此,在重载解析过程中也会被纳入考虑中
template<typename T> void processPointer(T* ptr); template<> void processPointer<void>(void*) = delete; template<> void processPointer<char>(char*) = delete;
有意思的是,如果在类里面有一个模板函数,则不能通过设置private来禁用一些实例化,因为不能给一个成员函数的模板特化一个不同于主模板的访问权限,例如
class Widget { public: ... template<typename T> void processPointer(T* ptr) {...} private: template<> void processPointer<void>(void*); // error };
问题在于模板特化必须被卸载命名空间范围内,而不是在类范围内,因此可以使用delete来实现
class Widget { public: ... template<typename T> void processPointer(T* ptr) {...} ... }; template<> void Widget::processPointer<void>(void*) = delete;
class Widget { public: ... void doWork() &; // only when *this is an lvalue void doWork() &&; // only when *this is an rvalue }; ... Widget makeWidget(); Widget w; ... w.doWork(); makeWidget().doWork();
显式地对成员函数声明override能使得编译器检查是否正确覆盖,而不是在没有正确覆盖时隐式地转换成了重载或者其他合法函数,而使得调用时发生意外调用,例如
class Base{ public: virtual void mf1() const; virtual void mf2(int x); virtual void mf3() &; void mf4() const; }; class Derived: public Base { public: virtual void mf1(); // not const virtual void mf2(unsigned int x); // not int virtual void mf3() &&; // not & void mf4() const; // not virtual in base };
虽然上面的函数都没有发生覆盖,但是有些编译器认为都是合法的,而不会给出警告,正确的做法是
class Derived: public Base { public: virtual void mf1() override; virtual void mf2(unsigned int x) override; virtual void mf3() && override; virtual void mf4() const override; };
此时,编译器能检查出所有的错误覆盖
class Point { public: constexpr Point(double xVal = 0, double yVal = 0) noexcept: x(xVal), y(yVal) {} constexpr double xValue() const noexcept { return x;} constexpr double yValue() const noexcept { return y;} void setX(double newX) noexcept { x = newX;} void setY(double newY) noexcept { y = newY;} private: double x, y; }; constexpr Point p1(9.4, 2.7); constexpr Point p2(28.8, 5.3); constexpr Point midpoint(const Point& p1, const Point& p2) noexcept { return { (p1.xValue() + p2.xValue()) / 2, (p1.yValue() + p2.yValue()) / 2 }; } constexpr auto mid = midpoint(p1, p2);
C++11中,setX和setY不能被声明为constexpr,因为不能在const成员函数中修改成员变量,而且返回值为void,并不是字面值常量,但是C++14中是允许的
[Effective Modern C++(11&14)]Chapter 3: Moving to Modern C++
标签:9.4 说明 guard field defs 依赖性 头文件 mpi 删除
原文地址:https://www.cnblogs.com/burningTheStar/p/8975712.html