C++ 中文周刊 第115期

周刊项目地址

公众号

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

提交 issue


资讯

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

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-05-24 第203期

五月邮件列表 https://isocpp.org/blog/2023/05/2023-05-mailing-available

文章

template <class...> struct whats_my_type;
int main() { whats_my_type<std::make_index_sequence<7>>{}; } 
// error: implicit instantiation of undefined template 
// ‘whats_my_type<std::integer_sequence<unsigned long, 0, 1, 2, 3, 4, 5, 6>’<source>:8:3: error: implicit instantiation of undefined template ‘whats_my_type<std::integer_sequence<unsigned long, 0, 1, 2, 3, 4, 5, 6>’

这玩意还是挺有用的

如果key已经是排好序的,插入可以O1

// Amortized linear overall insertion time if the range is sorted by key
// or reverse sorted by key. Performance degrades toward O(n log n) the more
// the list is not sorted.
template<typename C, typename Iterator>
auto try_emplace_mostly_sorted(C&& c, Iterator first, Iterator last)
    -> decltype(c.end())
{
    auto prev = c.end();
    for (; first != last; ++first) {
        prev = c.try_emplace(prev, first->key, first->value);
    }
    return prev;
}

llvm实现在这里,感兴趣的可以看看

https://github.com/llvm/llvm-project/blob/387c49f693c82bdf8b9b0f1ef48a92f51bb781b4/libcxx/include/__tree#L2027

新的编译器对constexpr放松要求,比如

#if __cpp_lib_optional >= 202106
constexpr
#endif
char xdigit(int n) {
  static constexpr char digits[] = "0123456789abcdef";
  return digits[n];
}

另外就是 optional unique_ptr variant,

<cmath> <cstdlib>

std::to_chars() std::from_chars()

以及constexpr new/delete

都支持constexpr

就是让枚举支持位运算组合,就像传统c的用法那样,但是枚举类是强类型,转换很不方便,怎么办?实现operator |

比如

enum class ERenderPass : uint8_t {
    None = 0,
    Geometry = 1 << 0,
    Lighting = 1 << 1,
    Particles = 1 << 2,
};
inline constexpr ERenderPass operator|(ERenderPass Lhs, ERenderPass Rhs) {
    return static_cast<ERenderPass>(
        static_cast<std::underlying_type_t<ERenderPass>>(Lhs) |
        static_cast<std::underlying_type_t<ERenderPass>>(Rhs));
}
ERenderPass Primary = ERenderPass::Geometry | ERenderPass::Lighting;

用宏更干净

#include <type_traits>

// Define bitwise operators for an enum class, allowing usage as bitmasks.
#define DEFINE_ENUM_CLASS_BITWISE_OPERATORS(Enum)                   \
    inline constexpr Enum operator|(Enum Lhs, Enum Rhs) {           \
        return static_cast<Enum>(                                   \
            static_cast<std::underlying_type_t<Enum>>(Lhs) |        \
            static_cast<std::underlying_type_t<Enum>>(Rhs));        \
    }                                                               \
    inline constexpr Enum operator&(Enum Lhs, Enum Rhs) {           \
        return static_cast<Enum>(                                   \
            static_cast<std::underlying_type_t<Enum>>(Lhs) &        \
            static_cast<std::underlying_type_t<Enum>>(Rhs));        \
    }                                                               \
    inline constexpr Enum operator^(Enum Lhs, Enum Rhs) {           \
        return static_cast<Enum>(                                   \
            static_cast<std::underlying_type_t<Enum>>(Lhs) ^        \
            static_cast<std::underlying_type_t<Enum>>(Rhs));        \
    }                                                               \
    inline constexpr Enum operator~(Enum E) {                       \
        return static_cast<Enum>(                                   \
            ~static_cast<std::underlying_type_t<Enum>>(E));         \
    }                                                               \
    inline Enum& operator|=(Enum& Lhs, Enum Rhs) {                  \
        return Lhs = static_cast<Enum>(                             \
                   static_cast<std::underlying_type_t<Enum>>(Lhs) | \
                   static_cast<std::underlying_type_t<Enum>>(Lhs)); \
    }                                                               \
    inline Enum& operator&=(Enum& Lhs, Enum Rhs) {                  \
        return Lhs = static_cast<Enum>(                             \
                   static_cast<std::underlying_type_t<Enum>>(Lhs) & \
                   static_cast<std::underlying_type_t<Enum>>(Lhs)); \
    }                                                               \
    inline Enum& operator^=(Enum& Lhs, Enum Rhs) {                  \
        return Lhs = static_cast<Enum>(                             \
                   static_cast<std::underlying_type_t<Enum>>(Lhs) ^ \
                   static_cast<std::underlying_type_t<Enum>>(Lhs)); \
    }
enum class ERenderPass : uint8_t {
    None = 0,
    Geometry = 1 << 0,
    Lighting = 1 << 1,
    Particles = 1 << 2,
    All = Geometry | Lighting | Particles,
};
DEFINE_ENUM_CLASS_BITWISE_OPERATORS(ERenderPass);

或者用enable_if 不用宏,有洁癖

#include <type_traits>

// Define a templatized struct to contain a bool constexpr that controls
// when the operators get generated.
template <typename E>
struct FEnableBitmaskOperators {
    static constexpr bool enable = false;
};

// This operator is only defined in the candidate set for a given type if the
// std::enable_if_t below evaluates to true, otherwise it is dropped.
template <typename E>
typename std::enable_if_t<FEnableBitmaskOperators<E>::enable, E> operator|(
    E Lhs, E Rhs) {
    return static_cast<E>(static_cast<std::underlying_type_t<E>>(Lhs) |
                          static_cast<std::underlying_type_t<E>>(Rhs));
}
// Rest of the operators...

With the following usage:

enum class ERenderPass : uint8_t {
    ...
};
// Specialize the struct to enable the operators for our enum.
template <>
struct FEnableBitmaskOperators<ERenderPass> {
    static constexpr bool enable = true;
};

// Works!
ERenderPass Primary = ERenderPass::Geometry | ERenderPass::Lighting;

实际上valkan cpp就是这么干的

template <typename BitType, typename std::enable_if<FlagTraits<BitType>::isBitmask, bool>::type = true>
VULKAN_HPP_INLINE VULKAN_HPP_CONSTEXPR Flags<BitType> operator|( BitType lhs, BitType rhs ) VULKAN_HPP_NOEXCEPT {
    return Flags<BitType>( lhs ) | rhs;
}

各取所需,我选第二种

手把手教你如何优化哈希表的API

优化find find经常会有一个检查iter的动作,非常多余,如果直接返回Optional<Value const&>,并且支持value_or,就干净的多

auto find(Key const&) -> iterator;
auto find(Key const&) -> Optional<pair<Key const, Value>&>;
auto find(Key const&) -> Optional<Value&>;
auto find(Key const&) -> Value;

但是find接口太多了。作者给了个特化建议

struct return_element {
    template <input_iterator I>
    static auto from_value(I const& it) -> Optional<iter_reference_t<I>> {
        return *it;
    }

    template <input_iterator I>
    static auto from_end(I const&) -> Optional<iter_reference_t<I>> {
        return {};
    }
};

struct return_value {
    template <input_iterator I>
    static auto from_value(I const& it) -> Optional<decltype(it->second)> {
        return it->second;
    }

    template <input_iterator I>
    static auto from_end(I const&) -> Optional<decltype(it->second)> {
        return {};
    }
};

struct return_value_or_zero {
    template <input_iterator I>
    static auto from_value(I const& it) -> decltype(auto(it->second))
        return it->second;
    }

    template <input_iterator I>
    static auto from_end(I const&) -> decltype(auto(it->second))
        return {};
    }
};
auto a = map.find(key);                        // iterator
auto b = map.find<return_element>(key);        // Optional<pair<Key const, Value>&>
auto c = map.find<return_value>(key);          // Optional<Value&>
auto d = map.find<return_value_or_zero>(key);  // Value

优化insert

insert可能不成功,所以需要推迟value构造

template <class F>
struct lazy_call {
    F f;

    template <class T> operator T() { return f(); }
};

#define LAZY(expr) lazy_call{[&]{ return expr; }}
auto [iter, success] = map.try_emplace(key, LAZY(acquire_value()));

这个思路还是很有用的

windows总是有这种稀奇古怪的错误,类似的有 shouldn’t name your DLL “security.dll

问题,给这个类写CTAD推导指引

template<class... Ts>
class Foo {
public:
  explicit Foo(Ts... as, Ts... bs) :
    a_(static_cast<Ts&&>(as)...),
    b_(static_cast<Ts&&>(bs)...) {}

private:
  std::tuple<Ts...> a_;
  std::tuple<Ts...> b_;
};

答案是当前还实现不了。。。不过可以实现一个make_foo https://godbolt.org/z/zTrcxnbbP

代码简单贴一下

template<class... TsTs>
struct foo_of_first_half {
  template<size_t I>
  using IthElement = std::tuple_element_t<I, std::tuple<TsTs...>>;

  template<size_t... Is>
  static auto f(std::index_sequence<Is...>)
      -> Foo<IthElement<Is>...>;

  static constexpr size_t N = sizeof...(TsTs) / 2;
  using type = decltype(f(std::make_index_sequence<N>()));
};

template<class, class>
struct is_doubled : std::false_type {};

template<class... Ts>
struct is_doubled<Foo<Ts...>, Foo<Ts..., Ts...>> : std::true_type {};

template<class... TsTs, class FooTs = typename foo_of_first_half<TsTs...>::type>
  requires is_doubled<FooTs, Foo<TsTs...>>::value
FooTs make_foo_guide(TsTs... tsts);

template<class... TsTs, class FooTs = decltype(make_foo_guide(std::declval<TsTs>()...))>
FooTs make_foo(TsTs&&... tsts) {
  return FooTs(std::forward<TsTs>(tsts)...);
}

看不懂

魔改clickhouse,也算有点意思

一段问题代码

extern concurrency::task<int> get_id_async();

void test(){
  try {
    get_id_async().then([=](int id) {
      return get_name_from_id_async(id);
    }).then([=](std::string name) {
      update_name(name);
    });
  } catch (...) {
    /* deal with the exception */
  }
}

问题出在哪里?get_name_from_id_async生成的task可能出现异常没捕获,不是函数,是函数生成的task本身

得改成这个

void test() {
  try {
    get_id_async().then([=](int id) {
      return get_name_from_id_async(id);
    }).then([=](std::string name) {
      update_name(name);
    }).then([=](concurency::task<void> precedent) {
      try {                                        
        precedent.get();                           
      } catch (...) {                              
        /* deal with exceptions in the tasks */    
      }                                            
    });                                            
  } catch (...) {
    /* deal with exceptions building the task chain */
  }
}

说实话我不是很懂,不如co

concurrency::task<void> test() {
  try {
    auto id = co_await get_id_async();
    auto name = co_await get_name_from_id_async(id);
    update_name(name);
  } catch (...) {
    /* deal with exceptions */
  }
}

我怎么感觉有人写过类似的?

template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };

template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

/// Starts the std::visit function
#define do_match std::visit(overloaded
/// The first part of a lambda statement
#define match_case(T) [&](T& var)
/// Closes the std::visit function passing in the matched value
#define match_value(value) , value)

struct IPv4 { std::string value; };
struct IPv6 { std::string value; };

int main() {

  std::variant<IPv4, IPv6> variant_value = IPv4{"192.168.0.1"};

  do_match {
     match_case(IPv4) { std::cout << "IPv4: " << var.value << std::endl; },
     match_case(IPv6) { std::cout << "IPv6: " << var.value << std::endl; },
     match_case(auto) { std::cout << "Any case" << std::endl; },
  } match_value(variant_value);

  return 0;
}

视频

这个讲的也是性能优化代码相关。比较常规。感兴趣可以看看。感觉翻来倒去还是那点玩意

另外还没入门的想学一下c++ 家人们 看这个 【-宵夜同学的个人空间-哔哩哔哩】 https://space.bilibili.com/1825238732

开源项目需要人手


本文永久链接

如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账