hilight.js

2018年5月4日金曜日

[c++]visual stadio 2012だけ 空shared_ptr構築の挙動が違う

visual studio 2012(以降vs2012)で、空のshared_ptrを生成するとuser_countで得られる値が、cpprefjpで確認した挙動と違ったので、確認してみるとvs2012だけ挙動が違ってたっぽい。

挙動を確認したコード

#include <iostream>
#include <memory>

template <class T, class P>
void print( T&& str, P&& p ) {
    std::cout << str << ":" << p.use_count() << " ";
    if ( p ) {
        std::cout << "exist" << std::endl;
    } else {
        std::cout << "null" << std::endl;
    }
}

int main()
{
    std::shared_ptr<int> p1;
    print( "p1", p1 );

    std::shared_ptr<int> p2( nullptr );
    print( "p2", p2 );

    std::shared_ptr<int> p3( nullptr, std::default_delete<int>() );
    print( "p3", p3 );

    std::shared_ptr<int> p4( nullptr, std::default_delete<int>(), std::allocator<void>() );
    print( "p4", p4 );

    std::shared_ptr<int> p5 = nullptr;
    print( "p5", p5 );
            
    std::shared_ptr<int> p6( new int );
    p6 = nullptr;
    print( "p6", p6 );

    return 0;
}

これを実行すると、cpprefjpの通りなら、デリータやアロケータを渡しているp3,p4だけ、use_countが1になるはず。

しかし、vs2012の結果はこれ。

Wandboxで、gccとclangを使ってコンパイルすると、ちゃんとリファレンス通りの結果になる。
gcc 4.7.3
clang 3.2
※コンパイラのバージョンはC++11に対応したできるだけ古いバージョンで比較

vs2015以降は、gccやclangと同じ結果になった。vs2013は知らない。
vs2012で空のshared_ptrを作りたい場合は、デフォルトコンストラクタを使おうってことだけど、もうvs2012自体使うことないか。

おまけ


shared_ptr クラス - MSDN - Microsoft visual studio 2015には、

null ポインターを使用して初期化される shared_ptr オブジェクトにはコントロール ブロックが存在し、空ではありません。

空の shared_ptr オブジェクトは、リソースを一切所有せず、コントロール ブロックも持ちません。

引数なし: 結果として生成されるオブジェクトは、空の shared_ptr オブジェクトまたは空の weak_ptr オブジェクトです。

デフォルトコンストラクタから生成されるのは、コントロールブロックもない空shared_ptrオブジェクトで、nullptrを渡した場合は、コントロールブロックが作られるので空オブジェクトじゃないと言ってるが、実装はこうなっていた。

constexpr shared_ptr() _NOEXCEPT
{    // construct empty shared_ptr
}

constexpr shared_ptr(nullptr_t) _NOEXCEPT
{    // construct empty shared_ptr
}

どう見ても両方共空shared_ptrオブジェクト生成しそうだが。
vs2012も同じ文章だったので、vs2012の実装に合わせて書いてそのままということなのだろうか。