前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >C++ 中文周刊 2024-11-16 第172期

C++ 中文周刊 2024-11-16 第172期

作者头像
王很水
发布2024-11-18 20:17:06
发布2024-11-18 20:17:06
8500
代码可运行
举报
运行总次数:0
代码可运行

资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-11-13 第280期

clang增加了一个safebuffer模式 https://clang.llvm.org/docs/SafeBuffers.html

可以使用 -Wunsafe-buffer-usage 目前还在开发中

clang增加了函数分析能力 https://clang.llvm.org/docs/FunctionEffectAnalysis.html

在noexcept 基础上增加了noblocking noallocating

更准确分析函数行为,可以配合之前介绍的RealTimeSan使用

主要问题是函数指针不行,函数指针/function自动丢弃上述属性

另外存在属性覆盖,noblocking noallocating的必要条件是noexcept,权限大于,大家懂我意思吧

函数指针怎么绕过

代码语言:javascript
代码运行次数:0
复制
std::sort(vec.begin(), vec.end(),
  [](const Elem& a, const Elem& b) [[clang::nonblocking]] { return a.mem < b.mem; }); // OK

static bool compare_elems(const Elem& a, const Elem& b) [[clang::nonblocking]] {
  return a.mem < b.mem; }; // 不行 属性会丢

std::sort(vec.begin(), vec.end(), compare_elems);

template <typename>
class nonblocking_fp;

template <typename R, typename... Args>
class nonblocking_fp<R(Args...)> {
public:
  using impl_t = R (*)(Args...) [[clang::nonblocking]];

private:
  impl_t mImpl{ nullptr_t };
public:
  nonblocking_fp() = default;
  nonblocking_fp(impl_t f) : mImpl{ f } {}

  R operator()(Args... args) const
  {
    return mImpl(std::forward<Args>(args)...);
  }
};

// deduction guide (like std::function's)
template< class R, class... ArgTypes >
nonblocking_fp( R(*)(ArgTypes...) ) -> nonblocking_fp<R(ArgTypes...)>;

// --

// Wrap the function pointer in a functor which preserves ``nonblocking``.
std::sort(vec.begin(), vec.end(), nonblocking_fp{ compare_elems });
代码语言:javascript
代码运行次数:0
复制

文章

  • • Use std::span instead of C-style arrays

https://www.sandordargo.com/blog/2024/11/06/std-span

我用得着你说?强调span不容易用错

  • • 复习一下c++98
代码语言:javascript
代码运行次数:0
复制
template<typename T>
class HasIsNullMethod {
    struct Yes { char unused[1]; };
    struct No { char unused[2]; };
    template <typename U, U u> struct reallyHas;
    template <typename C> static Yes& test(reallyHas<char (C::*)(), &C::isNull>* /*unused*/) { }
    template <typename C> static Yes& test(reallyHas<char(C::*)() const, &C::isNull>* /*unused*/) { }
    // template<class C> static Yes test(char(*)[static_cast<int>(&C::isNull == false) + 1]) {}
    template<class C> static No test(...);
public:
    static const bool Value = (sizeof(test<T>(0)) == sizeof(Yes));
};

struct A {
    char isNull() {}
};
struct B {
    void isNull() {}
};


struct C {
    char isNull;
};

bool fA() {
  return HasIsNullMethod<A>::Value;
}

bool fB() {
    return HasIsNullMethod<B>::Value;
}

bool fC() {
    return HasIsNullMethod<C>::Value;
}
// Type your code here, or load an example.
bool fint()
{
    return HasIsNullMethod<int>::Value;
}
#include <iostream>
int main() {
    std::cout << fint() << "\n";
    std::cout << fA()<< "\n";
    std::cout << fB()<< "\n";
    std::cout << fC()<< "\n";
    return 0;
}
代码语言:javascript
代码运行次数:0
复制


https://godbolt.org/z/hTaGja6cW

不会也没啥,糟粕 喜欢怀旧可以看一下

https://jguegant.github.io/blogs/tech/sfinae-introduction.html

  • • unique_ptr and the pointer to implementation idiom

https://andreasfertig.blog/2024/11/unique_ptr-and-the-pointer-to-implementation-idiom/

当实现pimpl惯用法的时候,使用unique_ptr通常因为看不到完整实现(析构)调用失败

作者给的办法是手动加上deleter

不要听他的,直接用shared_ptr,不要多此一举好吧

  • • What is the current time around the world? Utilizing std::chrono with time zones in C++23

https://www.cppstories.com/2024/chrono_dates_zones/

介绍timezone

我记得有一个date库就做了这个活,用不上可以直接用那个date

简单贴一下代码,介绍一下接口

代码语言:javascript
代码运行次数:0
复制
#include <chrono>
#include <print>

int main() {
    const auto now = std::chrono::system_clock::now();      
    auto zt_local = std::chrono::zoned_time{ std::chrono::current_zone(), now };
    std::print("now is {} UTC and local is: {}\n", now, zt_local);

    constexpr std::string_view Warsaw{ "Europe/Warsaw" };
    constexpr std::string_view NewYork{ "America/New_York" };
    constexpr std::string_view Tokyo{ "Asia/Tokyo" };

    try
    {
        const std::chrono::zoned_time zt_w{Warsaw, now};
        std::print("Warsaw: {0:%F} {0:%R}\n", zt_w);
        const std::chrono::zoned_time zt_ny{NewYork, now};
        std::print("New York: {0:%F} {0:%R}\n", zt_ny);
        const std::chrono::zoned_time zt_t{Tokyo, now};
        std::print("Tokyo: {0:%F} {0:%R}\n", zt_t);
    }
    catch (std::runtime_error& ex)
    {
        std::print("Error: {}", ex.what());
    }
}
/*
now is 2024-11-15 22:31:24.193993753 UTC and local is: 2024-11-15 22:31:24.193993753
Warsaw: 2024-11-15 23:31
New York: 2024-11-15 17:31
Tokyo: 2024-11-16 07:31
*/

https://godbolt.org/z/rEbfj69qf

还有其他代码,就不贴了

  • • C++, Complexity, and Compiler Bugs

https://azeemba.com/posts/cpp-complexity-compiler-bugs.html

这人不懂c++大惊小怪,就是简单的const延长生命周期

不看代码了。单纯喷他一下

  • • Memory error checking in C and C++: Comparing Sanitizers and Valgrind

https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind

老文章,valgrind不如sanitizer

  • • UB鉴赏环节

https://pvs-studio.com/en/blog/posts/cpp/1178/

死循环优化

代码语言:javascript
代码运行次数:0
复制
代码语言:javascript
代码运行次数:0
复制
#include <iostream>
     
int fermat () {
    const int MAX = 100;
    int a=1,b=1,c=1;
    int iter = 0;
    while (1) {
        if ( (a*a*a) == (b*b*b) + (c*c*c) ) {
            std::cout << "Found!\n";
            return 1;
        }
        a++;
        if (a>MAX) {
            a=1;
            b++;
        }
        if (b>MAX) {
            b=1;
            c++;
        }
        if (c>MAX) {
            c=1;
        }
        ++iter;
    }
    return 0;
}

int main () {
    if (fermat()) {
        std::cout << "Fermat's Last Theorem has been disproved.\n";
    } else {
        std::cout << "Fermat's Last Theorem has not been disproved.\n";
    }
    return 0;
}
代码语言:javascript
代码运行次数:0
复制

打印

代码语言:javascript
代码运行次数:0
复制
Found!
Fermat's Last Theorem has been disproved.
代码语言:javascript
代码运行次数:0
复制

由于return是死循环唯一出口,编译器激进到直接return 1

这个可以看lancern文章有介绍过

https://zhuanlan.zhihu.com/p/391088391

感谢zwuis提醒

析构栈溢出一个例子 https://godbolt.org/z/baaqjj39e

代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include <memory>
#include <vector>

struct Node {
    int value = 0;
    std::vector<Node> childrens;
};

struct List {
    int value = 0;
    std::unique_ptr<List> next;

    ~List() {
        while (next) {
            // The destructor is still recursive,
            // but now the recursion depth is 1 call.
            next = std::move(next->next);
        }
    }

    List() noexcept = default;
    List(List&&) noexcept = default;
    List& operator=(List&&) noexcept = default;
};

int main() {
    List dummynode;
    List* l = &dummynode;

    int BOUND = 1000;
    int SUB_BOUND = 100;
    for (int i = 1; i<BOUND; i++) {
        l->value = i;
        l->next = std::make_unique<List>();
        l = l->next.get();
    }
    /*
    // 这个析构会栈溢出
    Node n;
    auto tmp = &n;
    for (int i = 1; i<BOUND; i++) {
        for (int j = 0; j< SUB_BOUND; j++) {
            Node c;
            c.value = j*i;
            tmp->childrens[j] = c;
            // tmp = &tmp->childrens[j]; 
        }
    }
    */
}
代码语言:javascript
代码运行次数:0
复制

noexcept问题 noexcept=noexcept(true), noexcept(cond) 可以自己定制

成员函数除了析构都是noexcept(false) ,如果析构函数抛异常需要显式指明

代码语言:javascript
代码运行次数:0
复制
代码语言:javascript
代码运行次数:0
复制
struct SoBad {
  // invoke std::terminate
  ~SoBad() {
     throw std::runtime_error("so bad dtor");
  }
};

struct  NotSoBad {
  // OK
  ~NotSoBad() noexcept(false) {
    throw std::runtime_error("not so bad dtor");
  }
};
代码语言:javascript
代码运行次数:0
复制

使用noexcept需要你写异常验证代码,避免terminate爆炸

缓冲区溢出问题

一大堆傻逼函数 scanf strcpy strcat gets strncat等等,哦还有memcpy

引申出数组越界问题,数组越界会被激进优化,一定要注意

其实有点和前面的例子很相似

代码语言:javascript
代码运行次数:0
复制
代码语言:javascript
代码运行次数:0
复制
const int N = 10;
int elements[N];

bool contains(int x) {
  for (int i = 0; i <= N; ++i) {
    if (x == elements[i]) {
      return true;
    }
  }
  return false;
}

int main() {
  for (int i = 0; i < N; ++i) {
    std::cin >> elements[i];
  }
  return contains(5);
}
代码语言:javascript
代码运行次数:0
复制

存在越界 -> 越界是UB,编译器认为代码中没有UB,说明越界肯定不可达,说明提前返回 所以直接优化成true

类似例子

代码语言:javascript
代码运行次数:0
复制
代码语言:javascript
代码运行次数:0
复制
const int N = 10;
int main() {
  int decade[N];
  for (int k = 0; k <= N; ++k) {
    printf("k is %d\n",k);
    decade[k] = -1;
  }
}
代码语言:javascript
代码运行次数:0
复制

k越界,越界是UB,编译器认为代码中没有UB,说明永远到不了N,直接死循环

  • • Analyzing the Performance of the “Proxy” Library

https://devblogs.microsoft.com/cppblog/analyzing-the-performance-of-the-proxy-library/

鉴定为不如varint,我的机器上测试

代码语言:javascript
代码运行次数:0
复制
2024-11-16T23:09:24+08:00
Running ./benchmarks/msft_proxy_benchmarks
Run on (32 X 4499.92 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x16)
  L1 Instruction 32 KiB (x16)
  L2 Unified 1024 KiB (x16)
  L3 Unified 32768 KiB (x1)
Load Average: 0.65, 0.21, 0.11
---------------------------------------------------------------------------------------
Benchmark                                             Time             CPU   Iterations
---------------------------------------------------------------------------------------
BM_SmallObjectInvocationViaProxy                4648790 ns      5071349 ns          136
BM_SmallObjectInvocationViaVirtualFunction     11986710 ns     13076183 ns           54
BM_SmallObjectInvocationViaVariant              5044399 ns      5495963 ns          127
BM_LargeObjectInvocationViaProxy                7689574 ns      8388641 ns           83
BM_LargeObjectInvocationViaVirtualFunction      9397069 ns     10251350 ns           68
BM_LargeObjectInvocationViaVariant              5046724 ns      5490317 ns          127
BM_SmallObjectManagementWithProxy               1603751 ns      1749509 ns          402
BM_SmallObjectManagementWithUniquePtr           8952997 ns      9766806 ns           72
BM_SmallObjectManagementWithSharedPtr          11484836 ns     12528993 ns           56
BM_SmallObjectManagementWithSharedPtr_Pooled   14436982 ns     15749468 ns           44
BM_SmallObjectManagementWithAny                 6424830 ns      7008942 ns           96
BM_SmallObjectManagementWithVariant              514705 ns       561491 ns         1199
BM_LargeObjectManagementWithProxy              39497657 ns     43086918 ns           17
BM_LargeObjectManagementWithProxy_Pooled       30985407 ns     33760433 ns           21
BM_LargeObjectManagementWithUniquePtr          40788280 ns     44496508 ns           13
BM_LargeObjectManagementWithSharedPtr          50875412 ns     55500655 ns           11
BM_LargeObjectManagementWithSharedPtr_Pooled   30422979 ns     33188319 ns           21
BM_LargeObjectManagementWithAny                32133635 ns     35037580 ns           20
BM_LargeObjectManagementWithVariant            12639262 ns     13788144 ns           50
代码语言:javascript
代码运行次数:0
复制
这明显不如varint啊。还是观望吧
  • • How can I explicitly specialize a templated C++ constructor?

https://devblogs.microsoft.com/oldnewthing/20241011-00/?p=110365

如何显式特化构造函数,怎样让模板构造函数识别不同类型

实际上就是让构造函数作为工厂函数接口,工厂模式大家都熟悉,但是问题在于构造函数不能指定类型

比如这种实例

代码语言:javascript
代码运行次数:0
复制
代码语言:javascript
代码运行次数:0
复制
// Assume derived classes by convention have a constructor
// whose first parameter is an ObjectManager&.
struct CommonBase
{
    virtual ~CommonBase(){}
    virtual void initialize(int reason) = 0;
};

struct Widget : CommonBase
{
    Widget(int param) {}
    Widget(Widget&&) {}
    void initialize(int reason) {
        std::cout << "ok\n";
    }
     
};

struct ObjectManager
{
    // Concrete should derive from CommonBase
    template<typename Concrete, typename...Args>
    ObjectManager(int reason,
        std::in_place_type_t<Concrete>,
        Args&&...args) :
        m_base(std::make_unique<Concrete>(
                 std::forward<Args>(args)...))
    {
        m_base->initialize(reason);
    }
    template<typename Concrete, typename...Args>        
    static ObjectManager make(int reason, Args&&...args)
{                                                   
        return ObjectManager(reason,                    
            std::in_place_type_t<Concrete>{},               
            std::forward<Args>(args)...);              
    }  
    std::unique_ptr<CommonBase> m_base;
};

struct ObjectManager0
{
    // Concrete should derive from CommonBase
    template<typename Concrete, typename...Args>
    ObjectManager0(int reason, Args&&...args) :
        m_base(std::make_unique<Concrete>( std::forward<Args>(args)...))
    {
        m_base->initialize(reason);
    }

    std::unique_ptr<CommonBase> m_base;
};

// auto m0 = ObjectManager0<Widget>(9,42);
// auto m0 = ObjectManager0::ObjectManager0<Widget>(9,42);
auto m2 = ObjectManager::make<Widget>(9, 42);
代码语言:javascript
代码运行次数:0
复制

make显然就是工厂函数那种类型构造,但是构造函数没法指定类型

我们怎么给构造函数传递类型?用in_place_type_t

上面的代码还算容易看懂,其实就是tag把类型带过来,有点像identity_type那种玩法

不过构造函数模板不如类模板来的直观一些,这么玩有点复杂, 代码 https://godbolt.org/z/qj7YGox9a

  • • The Big Array Size Survey for C

https://thephd.dev/the-big-array-size-survey-for-c

数组长度命名,纠结老半天,了解历史可以看(没有看的必要)

  • • Non-allocating Circular Buffer in C++

https://mobiarch.wordpress.com/2024/11/15/non-allocating-circular-buffer-in-c/

数组 + 循环index,单线程。玩具,代码 https://github.com/bibhas2/CircusBuff/blob/master/circ-buff.h 自己看吧。懒得贴了。不值一看

  • • Unit Testing Numerical Routines https://buchanan.one/blog/testing-numerical-algorithms/

介绍一些代码测试经验,测试数据从何而来,如何更准确的测试接口,如何让测试代码更好的解释函数

感觉做图形学的哥们值得讲一下对应经验

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP每周推送 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 资讯
  • 文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档