小编Fur*_*ish的帖子

为什么在逗号分隔符上下文中将预增量的结果转换为 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
查看次数

使用 CRTP 实现单例

阅读此答案后,我尝试实现一些简单的 CRTP 用法。我想我会尝试实现 Singleton(是的,我知道 - 这只是为了练习和研究)模式,因为链接的答案已经做到了这一点......除了它无法编译的事实。

引用的代码如下:

template <class ActualClass> 
class Singleton
{
   public:
     static ActualClass& GetInstance()
     {
       if(p == nullptr)
         p = new ActualClass;
       return *p; 
     }

   protected:
     static ActualClass* p;
   private:
     Singleton(){}
     Singleton(Singleton const &);
     Singleton& operator = (Singleton const &); 
};
template <class T>
T* Singleton<T>::p = nullptr;

class A: public Singleton<A>
{
    //Rest of functionality for class A
};
Run Code Online (Sandbox Code Playgroud)

然后我将其“现代化”为:

template <class T>
class Singleton {
public:
    Singleton()                              = delete;
    Singleton(const Singleton&) …
Run Code Online (Sandbox Code Playgroud)

c++ singleton crtp

5
推荐指数
1
解决办法
2864
查看次数

是否可以在lambda签名通用中创建模板变量?

假设您有一个带有std::vector任何类型的函数并以某种方式处理它:

template<typename T>
void foo(std::vector<T> &vec) {
    // work with vec
}
Run Code Online (Sandbox Code Playgroud)

因为C++14,我们能够用lambdas实现同样的目标.在这种情况下,我们将它们称为通用lambdas,因为我们为它们引入了类似模板的推导:

auto foo_lambda = [](std::vector<auto> &vec) {
    // work with vec
};
Run Code Online (Sandbox Code Playgroud)

但我们的选择似乎对我很有限.假设我不仅要引入类型推导,还需要引入模板值.例如,让我们std::vector改为std::array:

template<typename T, std::size_t size>
void foo(std::array<T, size> &arr) {
    // work with arr
}
Run Code Online (Sandbox Code Playgroud)

在处理模板函数时,我们可以引入一个模板值,可以用来匹配参数的需求.整齐.

我想用通用lambda实现相同的功能,但我无法这样做.

有没有办法将一个类似的推导值引入lambda表达式,这样任何std::arrays都可以与lambda一起使用,类似于foo()上面函数的第二个版本?

编辑:正如Evg的评论所述,我的vector<auto>语法是非标准的GCC扩展.有关详情请参阅此答案参照这个文件.

c++ lambda templates generic-lambda c++17

5
推荐指数
1
解决办法
190
查看次数

将三路比较运算符的结果与 nullptr 进行比较有什么作用?

鉴于cppreference on<=>的示例,我们可以将示例代码简化为:

struct person {
    std::string name;
    std::string surname;

    auto operator <=> (const person& p) const {
        if (const auto result = name <=> p.name; result != 0) {
            return result;
        } else {
            return surname <=> p.surname;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

但是我的 IDE(CLion 2020.2)通过 clang-tidy 警告result != 0实际上应该是result != nullptr. 我不知道我们可以std::###_orderingnullptr. Cpprefence 还声称我们应该将它与literal 进行比较0。那里什么都nullptr没有。

这段代码:

int main() {
    person p1{"ccc", "vvv"};
    person p2{"aaa", "zzz"}; …
Run Code Online (Sandbox Code Playgroud)

c++ nullptr spaceship-operator c++20

5
推荐指数
1
解决办法
193
查看次数

创建自己的(可管道化的)范围 ::views 和 ::actions 的规则是什么?

这个答案中,我们可以看到我们可以创建自己的views. 我试过了:

template <typename Range>
struct squared_view : std::ranges::view_interface<squared_view<Range>> {
    struct iterator;

    constexpr squared_view() = default;
    constexpr squared_view(Range t): t(t) { }

    auto begin() const { return iterator(std::ranges::begin(t)); }
    auto end() const { return iterator(std::ranges::end(t)); }

    Range t;
};

template <typename Range>
struct squared_view<Range>::iterator {
    using value_type = typename std::ranges::iterator_t<Range>::value_type;

    constexpr iterator() = default;
    constexpr iterator(std::ranges::iterator_t<Range> it): it_{it} { }

    iterator& operator++() {
        ++it_;
        return *this;
    }

    iterator operator++(int) {
        const iterator current{*this};
        ++it_;
        return current; …
Run Code Online (Sandbox Code Playgroud)

c++ c++20 std-ranges

5
推荐指数
1
解决办法
210
查看次数

如何将 istream_view 收集到容器中?

我想实现我的扩展的通用减少操作ranges,将收集的任何元素range到一个给定的容器。为了实现这一点,我首先创建了一个用于提取template template参数的虚拟类型,并提供operator|了将 arange与其组合:

template <template <typename> typename T>
struct to_fn { };

template <template <typename> typename T>
inline constexpr detail::functors::to_fn<T> to;

template <template <typename> typename T>
auto operator|(std::ranges::range auto&& rng, detail::functors::to_fn<T>) {
    return T(std::ranges::begin(rng), std::ranges::end(rng));
}
Run Code Online (Sandbox Code Playgroud)

测试如下:

int main() {
    using namespace std::ranges;
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto set = vec | to<std::set>;
    static_assert(std::same_as<decltype(set), std::set<int>>);
    assert(equal(vec, set));
}
Run Code Online (Sandbox Code Playgroud)

代码完成执行没有问题。

但是,代码在与 一起使用时无法编译std::ranges::istream_view …

c++ containers c++20 std-ranges istream-view

5
推荐指数
1
解决办法
123
查看次数

为什么友元函数不被视为在没有额外声明的情况下声明它的类的名称空间的成员?

假设我们有一个foo来自命名空间的类space,它声明了一个friend名为 的函数bar,该函数稍后定义,如下所示:

namespace space {
    struct foo {
        friend void bar(foo);
    };
}

namespace space {
    void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
Run Code Online (Sandbox Code Playgroud)

据我了解,friend void bar(foo); 声明 bar是一个按值获取space的自由函数foo。要使用它,我们可以简单地执行以下操作:

auto f = space::foo();
bar(f);
Run Code Online (Sandbox Code Playgroud)

我的理解是,我们不必说space::bar,因为 ADL 会看到 的定义与(其参数)bar 相同namespacefoo,并允许我们省略全名限定。尽管如此,我们仍然可以对其进行限定

auto f = space::foo();
space::bar(f);
Run Code Online (Sandbox Code Playgroud)

其工作原理(并且应该工作)完全相同。

当我引入其他文件时,事情开始变得奇怪。假设我们将类和声明移动到foo.hpp

#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP …
Run Code Online (Sandbox Code Playgroud)

c++ namespaces friend header-files

5
推荐指数
1
解决办法
551
查看次数

MSVC 是否错误地处理依赖名称中的模板关键字?

知道我们必须在哪里以及为什么要放置templateandtypename关键字后,我惊讶地发现 MSVC接受以下代码:

struct foo {
    using bar = int;
};

template <typename T>
void quux() {
    T::template bar b;
}

int main() {
    quux<foo>();
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解, 的用法T::template bar b;是不正确的。声明的正确方法b是使用typename,如下所示:(typename T::bar b;而不是T::template bar b;)。template将意味着这bar是一个模板,但事实并非如此。

它是 MSVC 中的错误导致它接受不正确的代码,还是标准允许它并且 Clang 和 GCC 都没有以这种方式实现它(因此需要正确使用typename此处)?

c++ templates language-lawyer

5
推荐指数
1
解决办法
93
查看次数

为什么 std::ranges::upper_bound 不接受异构比较?

此代码有效并从向量返回一个foo{5}迭代器:

struct foo {
    int value;
};

auto main() -> int {
    auto ints = std::vector<foo>{{3}, {2}, {5}, {6}, {7}, {0}, {4}, {6}};

    std::ranges::sort(ints, {}, &foo::value);
    auto it = std::upper_bound(
            ints.begin(), ints.end(),
            4,
            [](const int v, const foo f) {
                return v < f.value;
            }
    );
}
Run Code Online (Sandbox Code Playgroud)

然而,这不能编译:

struct foo {
    int value;
};

auto main() -> int {
    auto ints = std::vector<foo>{{3}, {2}, {5}, {6}, {7}, {0}, {4}, {6}};

    std::ranges::sort(ints, {}, &foo::value);
    auto it = std::ranges::upper_bound(     // …
Run Code Online (Sandbox Code Playgroud)

c++ projection c++-concepts c++20 std-ranges

5
推荐指数
2
解决办法
397
查看次数

在移出向量上调用size()方法安全吗?

Standard指定STL容器在开始移动后(在本例中,我们说的std::move是启用移动构造/赋值)处于有效但未指定的状态

我相信这意味着我们只能应用不需要任何先决条件的操作。我记得这里有人在Stackoverflow上声称是真实的,经过一番检查我同意了。不幸的是,我不记得我检查了哪些资源。此外,我无法在标准中找到相关信息。

[container.requirements.general/4]62[tab:container.req])中,我们可以看到a.size()没有任何先决条件。这是否意味着此代码是安全的?

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = std::move(v1);

    std::cout << v1.size(); // displaying size of the moved-from vector
}
Run Code Online (Sandbox Code Playgroud)

这是未指定什么将这个代码打印,但它是安全的?意思是,我们这里有未定义的行为吗?

编辑:如果我问其他容器,我相信这个问题不会太广泛。答案在包括STL在内的所有其他STL容器中是否一致std::string

c++ stdvector move-semantics c++17

4
推荐指数
1
解决办法
96
查看次数

decltype(auto)是否会使尾随的返回类型过时?

已经有很多很多很多关于这些问题和答案尾随返回类型auto返回类型推演和非常有益的decltype(auto)。但我没能找到答案是否尾随返回类型,需要在所有的,因为我们有decltype(auto)。是否存在尾迹返回类型可以解决的情况,decltype(auto)要么无法使用,要么不起作用(给出意外的/错误的结果),并且首先需要尾迹返回类型?

c++ trailing-return-type c++17 c++20 decltype-auto

3
推荐指数
2
解决办法
166
查看次数

使用 std::forward 评估参数包的扩展究竟如何?

我想更好地理解参数包扩展,所以我决定研究一下,曾经对我来说很明显的东西,在试图理解到底发生了什么之后不再那么明显了。让我们检查一个标准的参数包扩展std::forward

template <typename... Ts>
void foo(Ts&& ... ts) {
    std::make_tuple(std::forward<Ts>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)

我的理解是,对于任何参数 pack Tsstd::forward<Ts>(ts)...将导致以逗号分隔的转发参数列表及其相应类型,例如,对于tsequal 1, 1.0, '1',函数体将扩展为:

std::make_tuple(std::forward<int&&>(1), std::forward<double&&>(1.0), std::forward<char&&>('1'));
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义。与函数调用一起使用的参数包扩展会生成以逗号分隔的调用该函数的列表,并带有适当的参数。

似乎困扰我的是operator,,如果我们想以类似的方式调用一堆函数,为什么有时需要引入逗号运算符 ( )?看到这个答案,我们可以阅读这段代码:

template<typename T>
static void bar(T t) {}

template<typename... Args>
static void foo2(Args... args) {
    (bar(args), ...); // <- notice: comma here
}

int main() {
    foo2(1, 2, 3, "3");
    return 0;    
}
Run Code Online (Sandbox Code Playgroud)

后跟将导致以下扩展的信息:

(bar(1), bar(2), bar(3), bar("3"));
Run Code Online (Sandbox Code Playgroud)

公平,有道理,但是……为什么?为什么这样做,而不是:

template<typename... Args> …
Run Code Online (Sandbox Code Playgroud)

c++ variadic-templates c++17

3
推荐指数
1
解决办法
203
查看次数

传递给 std::function 模板的模板参数究竟代表了什么?

std::function它本身提供了一个很好的实用程序——它提供了类型擦除,以便一般地存储/提供对可调用项的访问。它的灵活性非常好:

#include <functional>
#include <iostream>

void printer() {
    std::cout << "I print!";
}

int adder(int a, int b) {
    return a + b;
}

int main() {
    std::function<void()> fun1 = printer;       // fun1() calls printer()
    std::function<int(int, int)> fun2 = adder;  // fun2(1, 2) calls adder(1, 2)

    std::function<void()> fun3 = [](){};        // fun3() will do nothing - same for the lambda
    std::function<int(int, int)> fun4 =
            [](int a, int b) { return a + b; }; // fun4(1, 2) will yield …
Run Code Online (Sandbox Code Playgroud)

c++ function-pointers std-function

3
推荐指数
1
解决办法
88
查看次数