看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 ,,是以推导型的InputIt和Size这将导致一些令人吃惊的副作用。这可能是原因吗?如果是,我们确定演员void阵容完全解决了这个问题吗?
阅读此答案后,我尝试实现一些简单的 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) 假设您有一个带有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()上面函数的第二个版本?
鉴于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::###_ordering与nullptr. Cpprefence 还声称我们应该将它与literal 进行比较0。那里什么都nullptr没有。
这段代码:
int main() {
person p1{"ccc", "vvv"};
person p2{"aaa", "zzz"}; …Run Code Online (Sandbox Code Playgroud) 在这个答案中,我们可以看到我们可以创建自己的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 ++ 20的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 …
假设我们有一个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) 知道我们必须在哪里以及为什么要放置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此处)?
此代码有效并从向量返回一个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) 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?
我想更好地理解参数包扩展,所以我决定研究一下,曾经对我来说很明显的东西,在试图理解到底发生了什么之后不再那么明显了。让我们检查一个标准的参数包扩展std::forward:
template <typename... Ts>
void foo(Ts&& ... ts) {
std::make_tuple(std::forward<Ts>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)
我的理解是,对于任何参数 pack Ts,std::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) 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++ ×13
c++20 ×5
c++17 ×4
std-ranges ×3
templates ×2
c++-concepts ×1
casting ×1
containers ×1
crtp ×1
friend ×1
header-files ×1
istream-view ×1
lambda ×1
namespaces ×1
nullptr ×1
projection ×1
singleton ×1
std-function ×1
stdvector ×1