美文网首页c/c++
Effective Modern C++ - 3: 步入 Mod

Effective Modern C++ - 3: 步入 Mod

作者: my_passion | 来源:发表于2022-10-23 13:01 被阅读0次

item8 nullptr 优先于 0 和 NULL

总结:

(1) 0: 0 是 int 型, 编译器将 应该出现指针却出现0的位置上的0 勉强转换为 空指针 (void*)0

(2) NULL: C++ 中可能被编译器底层实现为 整型 (int/long)

Note: C 中, 还可能被编译器底层实现为空指针 (void*)0

(3) nullptr: 可 隐式转换所有/任意类型裸指针类型(进而可以被 智能指针形参 接收: 构造出 智能指针), 不会被视为任何整型

nullptr 的类型是 std::nullptr_t

std::nullptr_t 可隐式转换为 all pointer types

分析: 相对于 0/NULL,

1 nullptr 可避免 重载解析时的混淆/错误

0/NULL 作实参, 调重载函数 f(int/bool/void*) 时, 必然调用 f(int), 而非 f(void*) / 行为不确定

原因: 若 NULL 实现为 0L, 0L(long) -> int/bool/void*被编译器认为同样好

void f(int); // three overloads of f
void f(bool);
void f(void*);

f(0);       // calls f(int), not f(void*)

f(NULL);    // might not compile, but typically calls
            // f(int). Never calls f(void*)

f(nullptr); // calls f(void*) overload

2 nulptr 提高了代码的 清晰性, 尤其与 auto 结合时

(1) 用 0 看不出 函数的返回类型

auto result = findRecord( /* arguments */ );
if (result == 0) 
{
    …
}

(2) 用 0 能看出函数的返回类型必然为指针类型

auto result = findRecord( /* arguments */ );
if (result == nullptr) 
{
    …
}

3 nullptr 在模板中优势最大

lock + call

int f1(std::shared_ptr<Widget> spw);    // call these only when
double f2(std::unique_ptr<Widget> upw); // the appropriate
bool f3(Widget* pw);                    // mutex is locked
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxGuard = std::lock_guard<std::mutex>;
…
{
    MuxGuard g(f1m);        // lock mutex for f1
    auto result = f1(0);    // pass 0 as null ptr to f1
} // unlock mutex
…

{
    MuxGuard g(f2m);        // lock mutex for f2
    auto result = f2(NULL); // pass NULL as null ptr to f2
} // unlock mutex
…

{
    MuxGuard g(f3m);            // lock mutex for f3
    auto result = f3(nullptr);  // pass nullptr as null ptr to f3
} // unlock mutex

前2个没用 nullptr, 但也正确

Extract 1个模板来去重, 将 指针实参 抽象为 指针类型 PtrType => 0/NULL 作实参 推断出错误的类型 int/int 或 long, int/int 或 long 再试图传给 智能指针型形参的函数 f1/f2 时, 类型错误, 编译报错

nullptr 的类型是 std::nullptr_t => PtrType 为 std::nullptr_t, 再传给 f3时, std::nullptr_t 隐式转换为 Widget*

template<typename FuncType,
        typename MuxType,
        typename PtrType>
    auto 
lockAndCall(FuncType func,
            MuxType& mutex, // mutex 以引用传递
            PtrType ptr) -> decltype( func(ptr) )
{
    MuxGuard g(mutex);
    return func(ptr);
}
auto result1 = lockAndCall(f1, f1m, 0); // error!
…
auto result2 = lockAndCall(f2, f2m, NULL); // error!
…
auto result3 = lockAndCall(f3, f3m, nullptr); // fine

解决: 1/2用 nullptr 调用

例1: 0/NULL 能直接传给 智能指针: 先隐式转换为空指针, 再构造出智能指针

#include <mutex>
#include <memory>

class Widget
{
};

int f1(std::shared_ptr<Widget> spw)     // call these only when
{
    return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
    return 0.0;
}

bool f3(Widget* pw)                     // mutex is locked
{
    return false;
}

int main()
{
    std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
    using MuxGuard = std::lock_guard<std::mutex>;

    {
        MuxGuard g(f1m);        // lock mutex for f1
        auto result = f1(0);    // pass 0 as null ptr to f1
    } // unlock mutex

    {
        MuxGuard g(f2m);        // lock mutex for f2
        auto result = f2(NULL); // pass NULL as null ptr to f2
    } // unlock mutex

    {
        MuxGuard g(f3m);            // lock mutex for f3
        auto result = f3(nullptr);  // pass nullptr as null ptr to f3
    } // unlock mutex
}

例2: 0/NULL 不能 通过 1次模板实参推断后, 再通过隐式转换为空指针而间接传给智能指针

原因: 函数模板实例化过程中, 函数模板实参推断后, 只能按推断出的类型传递给另一函数, 不再进行隐式转换

#include <mutex>
#include <memory>

class Widget
{
};

int f1(std::shared_ptr<Widget> spw)     // call these only when
{
    return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
    return 0.0;
}

bool f3(Widget* pw)                     // mutex is locked
{
    return false;
}

using MuxGuard = std::lock_guard<std::mutex>;

template<typename FuncType,
    typename MuxType,
    typename PtrType>
    auto
lockAndCall(FuncType func,
        MuxType& mutex, // mutex 以引用传递
        PtrType ptr) -> decltype(func(ptr))
{
    MuxGuard g(mutex);
    return func(ptr);
}

int main()
{
    std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3

    auto result1 = lockAndCall(f1, f1m, 0);         // error!
    auto result2 = lockAndCall(f2, f2m, NULL);      // error!
    auto result3 = lockAndCall(f3, f3m, nullptr);   // fine
}
#include <mutex>
#include <memory>

class Widget
{
};

int f1(std::shared_ptr<Widget> spw)     // call these only when
{
    return 0;
}
double f2(std::unique_ptr<Widget> upw) // the appropriate
{
    return 0.0;
}

bool f3(Widget* pw)                     // mutex is locked
{
    return false;
}

using MuxGuard = std::lock_guard<std::mutex>;

template<typename FuncType,
    typename MuxType,
    typename PtrType>
    auto
lockAndCall(FuncType func,
        MuxType& mutex, // mutex 以引用传递
        PtrType ptr) -> decltype(func(ptr))
{
    MuxGuard g(mutex);
    return func(ptr);
}

int main()
{
    std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3

    auto result1 = lockAndCall(f1, f1m, nullptr);   // fine
    auto result2 = lockAndCall(f2, f2m, nullptr);   // fine
    auto result3 = lockAndCall(f3, f3m, nullptr);   // fine
}

item15 只要可能就用 constexpr: 编译期计算 / 运行期计算(不能进行编译期计算时, 自动降级为 运行期计算)

1 用于对象

constexpr auto arraySize2 = 10;     // fine, 10 is a

std::array<int, arraySize2> data2;  // fine, arraySize2
                                    // is constexpr

2 用于非成员函数

constexpr            // pow's a constexpr func
int pow(int base, int exp) noexcept // that never throws, base^exp
{
    return (exp == 0 ? 1 : base * pow(base, exp - 1) ); // C++11: 只支持函数体含1行语句, 可以是递归
}
constexpr int 
pow(int base, int exp) noexcept // C++14: 支持函数体含多行语句
{
    auto result = 1;
    for (int i = 0; i < exp; ++i) 
        result *= base;
    return result;
}

(1) 编译期值作实参 调用, 返回 编译期结果

constexpr auto numConds = 5; // # of conditions

std::array<int, pow(3, numConds)> results;  // results has
                                            // 3^numConds
                                            // elements

(2) 运行期值作实参 调用, 返回 运行期结果

auto base = readFromDB("base");     // get these values
auto exp = readFromDB("exponent");  // at runtime
auto baseToExp = pow(base, exp);    // call pow function
                                    // at runtime

3 用于成员函数: 运行期构造 + 调成员函数

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, 27.7);  // fine, "runs" constexpr
                                // ctor during compilation

constexpr Point p2(28.8, 5.3);  // also fine

constexpr
Point midpoint(const Point& p1, const Point& p2) noexcept
{
    return { (p1.xValue() + p2.xValue()) / 2,   
             (p1.yValue() + p2.yValue()) / 2 }; // call constexpr member funcs
}

constexpr auto mid = midpoint(p1, p2);  // init constexpr
                                        // object w/result of
                                        // constexpr function

item16 使 const 成员函数 线程安全

原因: const 成员函数修改 mutable 成员(如 cache + cacheIsValid) + 多线程 -> not 线程安全

remember

(1) 确保 const 成员函数线程安全, 除非你确定不会在并发 context 中使用

(2) std::atomic 变量可能比 mutex 性能高, 但只适用于操作单个 变量或内存位置

class Polynomial 
{
public:
    using RootsType = std::vector<double>;
    
    RootsType 
    roots() const
    {
        std::lock_guard<std::mutex> g(m); // lock mutex
        
        if (!rootsAreValid)         // if cache not valid
        {       
            …                       // compute, 
                                    // store them in rootVals
            rootsAreValid = true;
        }
        return rootVals;
    }
private:
    mutable std::mutex m;
    mutable bool rootsAreValid{ false }; // see Item 7 for initializers
    mutable RootsType rootVals{};       
};

相关文章

网友评论

    本文标题:Effective Modern C++ - 3: 步入 Mod

    本文链接:https://www.haomeiwen.com/subject/jweizrtx.html