C++03と11

次のソースをコンパイルして実行する。move関係はC++03のときはコメントアウトする。

#include <iostream>
#include <vector>

struct S { 
    int i;

    S () {
        std::cout << "default constructor" << std::endl;
    }   
    S (const int i) : i(i) {
        std::cout << "constructor: i=" << i << std::endl;
    }   
    S (const S & s) : i(s.i) {
        std::cout << "copy constructor: i=" << i << std::endl;
    }   
    S (const S && s) : i(s.i) {
        std::cout << "move constructor: i=" << i << std::endl;
    }   
    ~S () {
        std::cout << "destructor: i=" << i << std::endl;
    }   
    S & operator = (const S & s) {
        i = s.i;
        std::cout << "operator=(&): i=" << i << std::endl;
        return *this;
    }   
    S & operator = (const S && s) {
        i = s.i;
        std::cout << "operator=(&&): i=" << i << std::endl;
        return *this;
    }   
};

void printv (std::vector<S> & v) {
    for (std::vector<S>::iterator it = v.begin(); it != v.end(); ++it) {
        std::cout << it->i << ", ";
    }   
    std::cout << std::endl;
}

int main () {
    //std::vector<S> v = {1,2,3}; // initializer-list
    std::vector<S> v(3);
    v[0] = S(1);
    v[1] = S(2);
    v[2] = S(3);
    printv(v);
    std::vector<S>::iterator it = v.begin();
    std::advance(it, 1);
    v.erase(it);
    printv(v);
}

コンパイラのバージョン。

$ g++ --version
g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ clang++ --version
Ubuntu clang version 3.0-6ubuntu3 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

結果

$ g++ -std=c++03 -W -Wall -pedantic vec.cc 

$ ./a.out
default constructor
copy constructor: i=6303208
copy constructor: i=6303208
copy constructor: i=6303208
destructor: i=6303208
constructor: i=1
operator=(&): i=1
destructor: i=1
constructor: i=2
operator=(&): i=2
destructor: i=2
constructor: i=3
operator=(&): i=3
destructor: i=3
1, 2, 3, 
operator=(&): i=3
destructor: i=3
1, 3, 
destructor: i=1
destructor: i=3

$ clang++ -std=c++03 -W -Wall -pedantic vec.cc 

$ ./a.out
default constructor
copy constructor: i=4202384
copy constructor: i=4202384
copy constructor: i=4202384
destructor: i=4202384
constructor: i=1
operator=(&): i=1
destructor: i=1
constructor: i=2
operator=(&): i=2
destructor: i=2
constructor: i=3
operator=(&): i=3
destructor: i=3
1, 2, 3, 
operator=(&): i=3
destructor: i=3
1, 3, 
destructor: i=1
destructor: i=3

$ g++ -std=c++11 -W -Wall -pedantic vec.cc  

$ ./a.out
default constructor
default constructor
default constructor
constructor: i=1
operator=(&&): i=1
destructor: i=1
constructor: i=2
operator=(&&): i=2
destructor: i=2
constructor: i=3
operator=(&&): i=3
destructor: i=3
1, 2, 3, 
operator=(&&): i=3
destructor: i=3
1, 3, 
destructor: i=1
destructor: i=3

$ clang++ -std=c++11 -W -Wall -pedantic vec.cc

$ ./a.out                                     
default constructor
default constructor
default constructor
constructor: i=1
operator=(&&): i=1
destructor: i=1
constructor: i=2
operator=(&&): i=2
destructor: i=2
constructor: i=3
operator=(&&): i=3
destructor: i=3
1, 2, 3, 
operator=(&&): i=3
destructor: i=3
1, 3, 
destructor: i=1
destructor: i=3

結構違うもんやな(小並感)

元々、std::vector::erase()でコピーコンストラクタが呼ばれてないのを見て、不思議に思ったのが発端だったけど、それに関しては単にoperator=(const T &&)が呼ばれる、ということのようだ。

vのコンストラクタの挙動が03と11で異なるのは意外。03の方はstd::vector(size_type, const T & t = T())になっていて順次コピーしているように見えるが、11ではすべてデフォルトコンストラクタが呼ばれている。std::vector(size_type)が、std::vector(size_type, const T &)とは別に用意されているのだろうか。

規格と実装を見ながら比較できればいいんだけど、ちょっと時間がないのでここまで。