小编Fur*_*ish的帖子

在std :: remove_if执行期间遍历容器是否安全?

假设我想从中删除唯一元素std::vector(不消除重复项,而仅保留至少出现2次的元素),并且我想以一种非常低效的方式实现这一点-通过std::countstd::remove_ifing时调用。考虑以下代码:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 6, 3, 6, 2, 7, 4, 4, 5, 6};

    auto to_remove = std::remove_if(vec.begin(), vec.end(), [&vec](int n) {
        return std::count(vec.begin(), vec.end(), n) == 1;
    });

    vec.erase(to_remove, vec.end());

    for (int i : vec) std::cout << i << ' ';
}
Run Code Online (Sandbox Code Playgroud)

参考开始,std::remove_if我们知道从开始的元素to_remove具有未指定的值,但是我想知道它们到底有多未指定。

为了解释我所关心的远一点-我们可以看到,应删除的元素是1357-唯一的唯一值。std::remove_if会将移至 …

c++ algorithm stdvector

7
推荐指数
2
解决办法
134
查看次数

为什么 new 表达式可以正确生成指针类型,即使它应该返回 void*?

我们知道它void*没有关于它指向的数据的实际类型的信息。但是,从 cppreference 开始newnew[]我们知道这些运算符返回void*. 那么,如何给出:

auto x = new int{};
Run Code Online (Sandbox Code Playgroud)

知道new操作符应该返回void*x被推导为一种类型int*,不是void*吗?

再考虑一个例子:

struct foo {
    static void* operator new(std::size_t n) {
        std::cout << "new called!\n";
        return ::new char[n];
    }
};
Run Code Online (Sandbox Code Playgroud)

让我们添加一些牛逼YPE d isplayer:

template <typename T>
struct TD;
Run Code Online (Sandbox Code Playgroud)

和测试代码:

int main() {
    auto x = new foo{};

    TD<decltype(x)>{};
}
Run Code Online (Sandbox Code Playgroud)

代码无法编译,错误指示decltype(x)foo*。如果我们注释掉 的最后一行main,我们就会知道我们的void*-returning 操作符会因为new …

c++ void-pointers language-lawyer new-expression

7
推荐指数
1
解决办法
74
查看次数

为什么在逗号分隔符上下文中将预增量的结果转换为 void?

std::for_each_n可能的实现

template<class InputIt, class Size, class UnaryFunction>
InputIt for_each_n(InputIt first, Size n, UnaryFunction f)
{
    for (Size i = 0; i < n; ++first, (void) ++i) {
        f(*first);
    }
    return first;
}
Run Code Online (Sandbox Code Playgroud)

我注意到我们通常看到的部分i++(或首选++i)由两个操作组成:

  • ++first
  • (void) ++i

用逗号分隔。虽然大部分都说得通,但(void)演员阵容对我来说似乎有点令人惊讶。我只能猜测是,有可能是超载operator ,,是以推导型的InputItSize这将导致一些令人吃惊的副作用。这可能是原因吗?如果是,我们确定演员void阵容完全解决了这个问题吗?

c++ casting comma-operator

6
推荐指数
1
解决办法
85
查看次数

您可以(并且应该)消除使用 T 和 const 引用 T 的函数调用的歧义吗?

如果我们有:

void foo(int) {}
void foo(const int&) {}
Run Code Online (Sandbox Code Playgroud)

我们不能这样调用foo

foo(3);
Run Code Online (Sandbox Code Playgroud)

因为调用是模棱两可的:

error: call of overloaded 'foo(int)' is ambiguous
40 |     foo(3);
   |          ^
note: candidate: 'void foo(int)'
36 | void foo(int) {}
   |      ^~~
note: candidate: 'void foo(const int&)'
37 | void foo(const int&) {}
   |      ^~~
Run Code Online (Sandbox Code Playgroud)

我们可以做的是明确提供正确的重载,例如通过函数指针:

auto (*ptr)(const int&) -> void = foo;
ptr(3); // calls the "const int&" overload, obviously
Run Code Online (Sandbox Code Playgroud)

然而,这种方式违背了方便重载的目的。问题是 - 我可以以某种方式以更......优雅的方式消除呼叫的歧义吗?道路?是否有哪里会需要提供案件过载,为Tconst T& …

c++ overload-resolution

6
推荐指数
1
解决办法
118
查看次数

如果它是输入范围的最后一个元素,为什么 views::split 忽略分隔符后的空子范围?

对于以下代码

#include <iostream>
#include <string>
#include <ranges>

int main() 
{
    std::string s = " text ";
    auto sv = std::ranges::views::split(s, ' ');                    
    std::cout << std::ranges::distance(sv.begin(), sv.end());
}
Run Code Online (Sandbox Code Playgroud)

输出为 2。输出范围中不存在最后一个分隔符之后的空子范围。

这似乎不一致,因为N+1如果N输入范围中出现分隔符,我希望输出范围中有子范围。为什么不是这样?

请注意 range-v3 做的完全一样,所以我确定这是故意的,但我想知道为什么。

c++ range-v3 c++20 std-ranges

6
推荐指数
0
解决办法
147
查看次数

为什么使用整数类型约束 auto 使其不可用于 views::filter?

我写了一个简单的谓词,我想传递给std::ranges::views::filter

struct even_fn {
    constexpr
    bool operator()(std::integral auto&& e) const noexcept {
        return e % 2 == 0;
    }
};

inline constexpr even_fn even;
Run Code Online (Sandbox Code Playgroud)

示例用法:

using namespace std::ranges;
views::iota(1, 10) | views::filter(even);
Run Code Online (Sandbox Code Playgroud)

这失败了一大堆错误,我认为最有意义的错误是:

note: the expression 'is_invocable_v<_Fn, _Args ...> [with _Fn = even_fn&; _Args = {std::__detail::__cond_value_type<int>::value_type&}]' evaluated to 'false'
Run Code Online (Sandbox Code Playgroud)

但是,如果我std::integral从我的操作员中删除该部分(只留下auto&&),代码将成功编译。这是为什么?__cond_value_type当我们有约束函子时,它有什么特别之处呢?

c++ c++-concepts c++20

6
推荐指数
1
解决办法
86
查看次数

根据 Kotlin 中的条件将列表拆分为连续元素组

我正在尝试根据某种类型以及它们是否按顺序对列表进行分组

data class Item(val type: Int, val name: String)

private fun splitItems(items: List<Item>): List<List<Item>> {
    val groupedItems = mutableListOf<List<Item>>()
    var tempList = mutableListOf<Item>()
    items.mapIndexed { index, item ->
        if (index > 0) {
            val previousItem = items[index - 1]
            if (previousItem.type == item.type) {
                tempList.add(item)
            } else {
                if (tempList.isNotEmpty()) groupedItems.add(tempList)
                tempList = mutableListOf()
                tempList.add(item)
            }
        } else tempList.add(item)
    }
    if (tempList.isNotEmpty()) groupedItems.add(tempList)
    return groupedItems
}
Run Code Online (Sandbox Code Playgroud)

现在这个乐趣将需要

val items = mutableListOf(
    Item(1, "Shirt"),
    Item(1, "Shirt"),
    Item(2, "Pant"),
    Item(2, "Pant"),
    Item(2, …
Run Code Online (Sandbox Code Playgroud)

kotlin

6
推荐指数
1
解决办法
4611
查看次数

我可以通过placement-new覆盖const对象吗?

基本生活/8告诉我们,我们可以在一个对象的生命周期结束后,使用它所占用的存储空间来创建一个新的对象,并使用它原来的名称来引用它,除非:

\n
\n
    \n
  • 原始对象的类型不是 const 限定的,并且,如果是类类型,则不包含任何类型为 const 限定的非静态数据成员或引用类型,并且 [...]
  • \n
\n
\n

强调我的

\n

但是,就在其下方,我们可以看到一条注释:

\n
\n
    \n
  • 如果不满足这些条件,则可以通过调用从表示其存储地址的指针获得指向新对象的指针std\xe2\x80\x8b::\xe2\x80\x8blaunder
  • \n
\n
\n

这解释了的目的std::launder。我们可以有一个类类型const,并使用placement-new 来创建一个具有不同内部值的新对象。

\n

让我惊讶的是第一句话的第一部分。它清楚地表明,如果存储是const(不一定包含const成员,但整个对象被声明为const),我们不能用它来引用一个新对象,但这可能意味着std::launder可以修复它。

\n

但我们如何创建这样的对象呢?最简单的例子失败了:

\n
const auto x = 5;\nnew (&x) auto(10);\n
Run Code Online (Sandbox Code Playgroud)\n

这是因为我们不能用作const void*新放置的缓冲区。我们可以const_cast做到,但抛弃“真实”const性就是未定义的行为。我相信这里也是如此。

\n

如果只是禁止使用const对象作为放置新缓冲区,我会理解,但如果是这样,那么第一个引用中强调的部分的目的是什么?我们能否真正利用重用const对象的存储重用于不同的对象吗?

\n

c++ language-lawyer stdlaunder

6
推荐指数
1
解决办法
399
查看次数

字节数组的放置新(隐式创建对象)是否会结束先前占用该存储的对象的生命周期?

P0593,在类型双关部分下,展示了这个例子:

float do_bad_things(int n) {
  alignof(int) alignof(float)
    char buffer[max(sizeof(int), sizeof(float))];
  *(int*)buffer = n;      // #1
  new (buffer) std::byte[sizeof(buffer)];
  return *(float*)buffer; // #2
}
Run Code Online (Sandbox Code Playgroud)

并指出:

提议的规则将允许一个int对象突然出现以使第 #1 行有效 [...],并且将允许一个float对象同样突然出现以使第 #2 行有效。

然而,这些示例在提议的规则下仍然没有定义行为。原因是 [basic.life]p4 的结果:

本文档中赋予对象和引用的属性仅在给定对象或引用的生命周期内适用。

具体来说,对象所持有的值仅在其整个生命周期内保持稳定。int当第 #1 行中的对象的生命周期结束时(当第 #2 行中的对象重用其存储时float),它的值就消失了。对称地,当创建 float 对象时,该对象具有不确定的值 ([dcl.init]p12),因此任何加载其值的尝试都会导致未定义的行为。

强调我的

该提案声称有问题的部分是float对象的(隐式)创建。但是上一行 ( new (buffer) std::byte[sizeof(buffer)]) 不是已经重用了存储(通过创建数组),结束了相关byte的生命周期吗?int据我了解,安置总是新的结束内存中创建新对象的对象的生命周期。

另外,这个评论说“新表达式不承诺保留存储中的字节。 ”这是否意味着new (buffer) std::byte[sizeof(buffer)]理论上可以改变 …

c++ lifetime placement-new language-lawyer c++23

6
推荐指数
1
解决办法
147
查看次数

就特定视图的使用而言,std::ranges::algorithm 与基于范围的 for() 循环相比在语义上有所不同

我想逐列查看 3x3 网格,所以我想我会std::views::stride像这样使用:

#include <array>
#include <iostream>
#include <ranges>

auto main() -> int {
    auto grid = std::array<std::array<int, 3>, 3>{{
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
    }};

    namespace vs = std::views;

    auto strideCount = grid.size();
    auto allElements = grid | vs::join;

    auto columns = vs::iota(0uz, strideCount)
                   | vs::transform([allElements, strideCount](auto n) {
                        return allElements
                                | vs::drop(n)
                                | vs::stride(strideCount);
                   });

    for (auto&& column : columns) {
        for (auto element : column) {
            std::cout << element << ' …
Run Code Online (Sandbox Code Playgroud)

c++ for-loop std-ranges c++23

6
推荐指数
1
解决办法
301
查看次数