Информация об изменениях

Сообщение проблема с move семантикой от 27.08.2018 21:16

Изменено 27.08.2018 21:24 Vinick

проблема с move семантикой
Помогите разобраться, что происходит и что я сделал неправильно.
  Код
#include <iostream>
struct Dim;
struct Group;

struct Cross {
  int i;
  Cross(int l):i(l) { std::cout << "Cross() = " << (uint64_t)this << std::endl;}
  Dim dim();
};

struct Dim {
  Cross * ptr;
  explicit  Dim(Cross * c):ptr(c) {
    std::cout << "Dim(Cross*) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Dim(Dim && d):ptr(d.ptr) {
    std::cout << "Dim(&&) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Group group();
};

struct Group {
  Dim * ptr;
  explicit Group(Dim * c):ptr(c) {
    std::cout << "Group(Dim*) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Group(Group && d):ptr(d.ptr) {
    std::cout << "Group(&&) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }

};

Dim Cross::dim() {
  Dim d(this);
  return d;
}

Group Dim::group() {
  Group g(this);
  return g;
}

template<typename D>
struct Op {
  D g;
  Op(Op && o):g(std::move(o.g)) {
    std::cout << "Op(&&) = " << (uint64_t)this << " : " << (uint64_t)g.ptr << std::endl;
  }
  Op(D && gg):g(std::move(gg)) {
    std::cout << "Op(D&&) = " << (uint64_t)this << " : " << (uint64_t)g.ptr << std::endl;
  }
};

template<typename D>
struct Cons {
  D op;
  Cons(Cons && c):op(std::move(c.op)) {
    std::cout << "Cons(&&) = " << (uint64_t)this << " : " << (uint64_t)op.g.ptr << std::endl;
  }
  explicit  Cons(D && gg):op(std::move(gg)) {
    std::cout << "Cons(Op&&) = " << (uint64_t)this << " : " << (uint64_t)op.g.ptr << std::endl;
  }
};

template<typename D>
decltype(auto) make_consumer1(D &&d) {
  auto op = Op<decltype(d)>(std::move(d));
  return Cons<decltype(op)>(std::move(op));
}
template<typename G>
decltype(auto) make_consumer2(G && g) { // <---------если здесь передавать аргумент по значению, то проблема исчезает.
  auto gop = Op<decltype(g)>(std::move(g));
  return Cons<decltype(gop)>(std::move(gop));
}


int main() {
  Cross c(10);
  auto cons1 = make_consumer1(c.dim());
  std::cout << "cons1 "<< cons1.op.g.ptr->i << std::endl;
  auto cons2 = make_consumer2(cons1.op.g.group());
  std::cout << cons2.op.g.ptr->ptr->i << std::endl; // <------------ ERROR: должно напечатать 10, а печатает мусор
  return 0;
}


Код выводит мусор. Address sanitizer сообщает "stack-use-after-scope". Проблема воспроизводится только на GCC (проверял на 8.2.0 и 5.4) и только с оптимизацией -O0 или -O1.
Что интересно на он-лайн компиляторах проблемы нет.
проблема с move семантикой
Помогите разобраться, что происходит и что я сделал неправильно.
  Код
#include <iostream>
struct Dim;
struct Group;

struct Cross {
  int i;
  Cross(int l):i(l) { std::cout << "Cross() = " << (uint64_t)this << std::endl;}
  Dim dim();
};

struct Dim {
  Cross * ptr;
  explicit  Dim(Cross * c):ptr(c) {
    std::cout << "Dim(Cross*) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Dim(Dim && d):ptr(d.ptr) {
    std::cout << "Dim(&&) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Group group();
};

struct Group {
  Dim * ptr;
  explicit Group(Dim * c):ptr(c) {
    std::cout << "Group(Dim*) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }
  Group(Group && d):ptr(d.ptr) {
    std::cout << "Group(&&) = " << (uint64_t)this << " : " << (uint64_t)ptr << std::endl;
  }

};

Dim Cross::dim() {
  Dim d(this);
  return d;
}

Group Dim::group() {
  Group g(this);
  return g;
}

template<typename D>
struct Op {
  D g;
  Op(Op && o):g(std::move(o.g)) {
    std::cout << "Op(&&) = " << (uint64_t)this << " : " << (uint64_t)g.ptr << std::endl;
  }
  Op(D && gg):g(std::move(gg)) {
    std::cout << "Op(D&&) = " << (uint64_t)this << " : " << (uint64_t)g.ptr << std::endl;
  }
};

template<typename D>
struct Cons {
  D op;
  Cons(Cons && c):op(std::move(c.op)) {
    std::cout << "Cons(&&) = " << (uint64_t)this << " : " << (uint64_t)op.g.ptr << std::endl;
  }
  explicit  Cons(D && gg):op(std::move(gg)) {
    std::cout << "Cons(Op&&) = " << (uint64_t)this << " : " << (uint64_t)op.g.ptr << std::endl;
  }
};

template<typename D>
decltype(auto) make_consumer1(D &&d) {
  auto op = Op<decltype(d)>(std::move(d));
  return Cons<decltype(op)>(std::move(op));
}
template<typename G>
decltype(auto) make_consumer2(G && g) { // <---------если здесь передавать аргумент по значению, то проблема исчезает.
  auto gop = Op<decltype(g)>(std::move(g));
  return Cons<decltype(gop)>(std::move(gop));
}


int main() {
  Cross c(10);
  auto cons1 = make_consumer1(c.dim());
  std::cout << "cons1 "<< cons1.op.g.ptr->i << std::endl;
  auto cons2 = make_consumer2(cons1.op.g.group());
  std::cout << cons2.op.g.ptr->ptr->i << std::endl; // <------------ ERROR: должно напечатать 10, а печатает мусор
  return 0;
}


Код выводит мусор. Address sanitizer сообщает "stack-use-after-scope". Проблема воспроизводится только на GCC (проверял на 8.2.0 и 5.4) и только с оптимизацией -O0 или -O1,
точнее на высоких уровнях оптимизации печаетается правильный результат, но AddressSanitizer все равно ругается.