这个系列是从这篇博客开始的,主要是复现Jason Turner的“C++ Weekly With Jason Turner”视频中的代码。
013 Fabonacci
这个例子挺有意思,首先展示了Fabonacci数列如果用普通的递归方式设计,函数的调用次数将会成级数增加。同时Fabonacci数列的数值增加是很快的,几十个以后数据的范围就超出了int类型能够表达的范围了。Fabonacci数列参考这里。
#include <iostream>
static int fib( int i ) {
if ( 0 == i ) return 0;
if ( 1 == i ) return 1;
return fib( i - 2 ) + fib( i - 1 );
}
int main() {
std::cout << "Hello, Fibonacci! \n";
const int i = 46;
std::cout << "The " << i << "th Fibonacci number is " << fib(i) << '\n';
return 0;
}
上述代码经过/usr/bin/time执行的结果是
Hello, Fibonacci!
The 46th Fibonacci number is 1836311903
5.28user 0.00system 0:05.28elapsed 99%CPU (0avgtext+0avgdata 3356maxresident)k
0inputs+0outputs (0major+134minor)pagefaults 0swaps
之后,Jason以计算Fabonacci数列作为task,试图将前47个(从零开始)数值在编译时创建,并在运行时访问。最终所采用的手段是variadic template + std::integer_sequence类型。其中涉及到template value argument的variadic形式,以及sizeof()函数在variadic template时使用的情形挺有意思。
#include <array>
#include <iostream>
#include <utility>
template < size_t I >
struct Fib {
static const int value = Fib<I-2>::value + Fib<I-1>::value;
};
template <>
struct Fib<0> {
static const int value = 0;
};
template <>
struct Fib<1> {
static const int value = 1;
};
template < size_t... I >
static const int fib_impl( std::index_sequence<I...>, int i ) {
constexpr std::array< int, sizeof...(I) > fibs = {
Fib<I>::value... };
std::cout << "sizeof...(I) = " << sizeof...(I) << '\n';
return fibs[i];
}
const int fib(int i) {
return fib_impl( std::make_index_sequence<47>(), i );
}
int main() {
std::cout << "Hello, TemplateFibonacci! \n";
std::cout << fib(46) << '\n';
return 0;
}
同样采用/usr/bin/time执行的结果如下
Hello, TemplateFibonacci!
sizeof...(I) = 47
1836311903
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 3372maxresident)k
0inputs+0outputs (0major+134minor)pagefaults 0swaps
014 Exchange
这一期里面有提到std::next()的使用,但是我感觉我平时也用不到,这里不复现Jason的代码了。std::exchange()挺有意思的。我测试了一下std::exchange()尤其是观察它结合move semantics的行为。测试结果表明某些时候std::exchange()会进行一次move construction,应该是创建了一个临时对象。具体结果见下面代码和测试输出。
#include <iostream>
#include <string>
#include <utility>
struct A {
A()
:val{0}, name{""} {}
~A() = default;
A(const A& other) {
this->val = other.val;
if ( name.empty() ) {
std::cout << "Copy construct. " << '\n';
} else {
std::cout << name << ": Copy construct" << '\n';
}
}
A( A&& other ) {
this->val = other.val;
other.val = -1;
if ( name.empty() ) {
std::cout << "Move construct. " << '\n';
} else {
std::cout << name << ": Move construct. " << '\n';
}
}
A& operator = ( const A& other ) {
if ( &other == this ) {
return *this;
}
this->val = other.val;
if ( name.empty() ) {
std::cout << "Assignment. " << '\n';
} else {
std::cout << name << ": Assignment. " << '\n';
}
return *this;
}
A& operator = ( A&& other ) {
if ( &other == this ) {
return *this;
}
this->val = other.val;
if ( name.empty() ) {
std::cout << "Move assignment. " << '\n';
} else {
std::cout << name << ": Move assigment. " << '\n';
}
return *this;
}
friend std::ostream& operator << ( std::ostream& os, const A& a) {
os << a.val;
return os;
}
int val;
std::string name;
};
int main() {
std::cout << "Hello, Exchange! \n";
int a = 1;
int b = std::exchange( a, 2 );
std::cout << "a = " << a << '\n';
std::cout << "b = " << b << '\n';
A objA;
objA.val = 1;
std::cout << "objA = " << objA << '\n';
A objB(objA);
std::cout << "objB = " << objB << '\n';
A objC( std::move(objB) );
std::cout << "objC = " << objC << '\n';
std::cout << "objB = " << objB << '\n';
objC.val = 2;
std::cout << "objD = objC;" << '\n';
A objD;
objD = objC; // Assignment.
std::cout << "objD = " << objD << '\n';
std::cout << "A objE = objC;" << '\n';
A objE = objC; // This invokes the copy constructor, not the assignment operator.
std::cout << "objE = " << objE << '\n';
std::cout << "objF = std::move(objE);" << '\n';
A objF;
objF = std::move(objE); // Move assignment.
std::cout << "objF = " << objF << '\n';
std::cout << "A objG = std::move(objF);" << '\n';
A objG = std::move(objF); // This inokes the move constructor, not the move assigment operator.
std::cout << "objG = " << objG << '\n';
std::cout << '\n';
objG.name = "objG";
objF.name = "objF";
std::cout << "objH = std::exchange(objG, objF);" << '\n';
A objH;
objH.name = "objH";
objH = std::exchange(objG, objF);
std::cout << "objI = std::exchange(objG, std::move(objF));" << '\n';
A objI;
objI.name = "objI";
objI = std::exchange(objG, std::move(objF));
std::cout << '\n';
std::cout << "A objJ = std::exchange( objG, objF );" << '\n';
A objJ = std::exchange( objG, objF );
std::cout << "A objJ = std::exchange( objG, std::move(objF) );" << '\n';
A objK = std::exchange( objG, std::move( objF ) );
return 0;
}
测试输出如下
Hello, Exchange!
a = 2
b = 1
objA = 1
Copy construct.
objB = 1
Move construct.
objC = 1
objB = -1
objD = objC;
Assignment.
objD = 2
A objE = objC;
Copy construct.
objE = 2
objF = std::move(objE);
Move assignment.
objF = 2
A objG = std::move(objF);
Move construct.
objG = 2
objH = std::exchange(objG, objF);
Move construct.
objG: Assignment.
objH: Move assigment.
objI = std::exchange(objG, std::move(objF));
Move construct.
objG: Move assigment.
objI: Move assigment.
A objJ = std::exchange( objG, objF );
Move construct.
objG: Assignment.
A objJ = std::exchange( objG, std::move(objF) );
Move construct.
objG: Move assigment.

本文探讨了使用C++模板来高效计算斐波那契数列的方法,对比了递归和模板元编程的效率差异,并深入研究了std::exchange函数在不同情况下的行为表现。
303

被折叠的 条评论
为什么被折叠?



