CppWeekly 02,Fabonacci, exchange

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

这个系列是从这篇博客开始的,主要是复现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. 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值