cpp进阶

@vrqq  February 8, 2020

assert(expression) 在Release模式下,是直接跳过。。

#ifdef NDEBUG
    #define assert(expression) ((void)0)

lambda表达式引用传值需要看被引用object的寿命

void func() {
    std::string *sptr = new std::string{};
    return std::async(std::launch::async , [&](){ sptr->size(); });
}

上述错在 sptr在func()执行完就释放了,应该改成=复制

deque::end()也会随着erase行为变化,看源码不够,要看standard

std::deque<int> Q;
Q.push(xxx);
auto endit = Q.end();
while(Q.begin() != endit)
  Q.pop_front();

这段代码在clang libcxx里面没问题,再gcc libstdc++也没问题,但是在mvsc STL就表现不一致了!
我们来看isocpp standard:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1463r0.pdf

Effects: An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque. [Note: pop_front and pop_back are erase operations. — end note

past-the-end iterator指的是 deque.end()
(没看过standard只看过代码可不能叫精通c++啊嘿嘿嘿嘿嘿)

std::move是信任
从一个常见用法看起

std::vector<std::future<void>> subprocess_waiter;
while(mybool) {
    auto subproc = std::async(std::launch::async, my_function);
    subprocess_waiter.emplace_back(std::move(subproc));
}

当emplace_back有&&方法时候,说明它可以接受一个右值引用,这时我们可以放心大胆的 “信任” 这个vector一定能接住值!
(无论其内部到底是怎么move,还是用copy假move都不要紧。)
也就是说一旦支持&&(传入右值引用),那么后面subproc析构也没影响!

其实它内部实现方法是,由vector接住&&以后,再把&&传给,如果future接不住,那编译时就提示失败。
也就是说,我们在写程序时候,但凡声明了func(typeA &&_input),就要保证_input以后啥时候销毁我们的函数都不会崩。

再来看一个std::make_move_iterator(iterator)
以vector举例,它把iterator变成&&,但是这里移动不是vector在做,而是vector所包含的string的行为!

vector<string> a{"11", "22"};
vector<string> b(make_move_iterator(a.begin()), make_move_iterator(a.end()));
// a={"",""} b={"11", "22"} here.

这里可以看出,是vector通知string一个个调用string(&&),终归是躲不开遍历的。
如果把string换成int,两个数组都一样。(因为int的move和copy没差)

Windows下 fstream的 ios::binary参数
无论用<<还是fout.write方式写,如果stream不加ios::binary,windows遇到\LF就会自动在前面插\CR,我说咋文件这么大。。详见文档:https://docs.microsoft.com/en-us/cpp/standard-library/binary-output-files?view=vs-2019
实测写一段binary进文件,里面有一个"\x0a"于是就变成了"\0x0d\x0a"
(ios::out | ios::binary)小心不要写成||`

类型转换后delete会怎么样 例如delete void*
未定义行为

void *p = new struct M{};
delete p; //undefined behavior.

参考如下:https://blog.csdn.net/yangguihao/article/details/49507803

delete-expression: ::opt delete cast-expression ::opt delete [ ]
cast-expression

In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a
base class of the operand’s dynamic type and the static type shall
have a virtual destructor or the behavior is undefined. In the second
alternative (delete array) if the dynamic type of the object to be
deleted differs from its static type, the behavior is undefined.
所以:如果想用基类指针删除子类,需要析构函数是virtual的,从而可以找到子类。
其他的类型转换的delete是未定义行为。

一个struct,为什么deque可以而vector不能emplace_back()

struct My {
  My(int a) {}
};

std::vector<My> arr;
arr.emplace_back(12); //compile error.

我也很懵,编译器提示非要有copy constructor 或者 move constructor
去StackOverflow里看有人说能用deque 但为啥呢?
看到这个恍然大悟:https://zhuanlan.zhihu.com/p/53799565
因为vector内存不够时会重开内存,移过去,这时就会调用copy/move构造器

一些 atomic 用法
利用cas指令把多个if原子化

// if (x > 0)
//     x += 20;
atomic<int> x;
int last = 999; //the most probility number. (positive)
while (x.compare_exchange_strong(last, last + 20) == false) {
    if (last <= 0)
        break;
}

// if (flag_start == false)
//    flag_start = true;
// else
//    return;
atomic<bool> flag_start;
if (flag_start.exchange(true) == true)
    return;

// if (state == 'T')
//     return;
// if (state == 'I')
//     state = 'R';
atomic<char> state;
char last = 'I';
while(state.compare_exchage_strong(last, 'R')) {
    if (last == 'T')
        return;
    last = 'I';
}

ATOMIC的下一种典型用法
上游有多个线程并行调用 feed()函数,其内部先修改某个变量,然后推送给下游。现在我们想仅同时有一个线程推送给下游,并且尽量推送最新的修改后的变量内容。(既多次修改推送最后一次)
市面上常见的做法有新开一个eventloop线程向下推送(例如驱动程序的下半场),但我们这里不需要大缓冲区,同时也可减少不必要的线程切换,所以用atomic即可。

double my_var;
std::mutex mtx;

constexpr char bCurrentThread = 0b1;
constexpr char bUpdated = 0b10;
std::atomic<char> report_flag = 0;

//thread safe function
void feed(double new_value) {
    //set
    mtx.lock();
    report_flag.fetch_or(bUpdated);
    my_var = new_value;
    mtx.unlock();

    //report
    //  1) race the report thread (only one thread in parallel)
    //  2) stop until bUpdated clear
    if (report_flag.fetch_or(bCurrentThread) & bCurrentThread) {
        char want = bCurrentThread;
        while(!report_flag.compare_exchange_strong(want, 0)) {
            while(report_flag.load() != bCurrentThread) {
                mtx.lock();
                auto tmp = my_var;
                report_flag.fetch_and(~bUpdated);
                mtx.unlock();
                
                //feed to downstream
                downstream->thread_unsafe_tell(tmp);
            }
            want = bCurrentThread;
        }
    }
} //feed()

一些SFINAE
https://blog.vrqq.org/archives/801/


添加新评论