Комментарии 19
boost 4.6? Вы подразумевали 1.46? Если да, то почему такой старый? Если нет, то что за буст вы имели в виду?
Debian Wheezy, Boost 1.49. Мне удается скомпилировать следующий код версиями gcc 4.4, 4.6, 4.7, 4.8, 4.9 (из Debian Wheezy и Jessie) как с опцией -std=c++11 / -std=c++0x так и без неё. Подозреваю, что на Squeeze с его Boost 1.42 тоже всё будет компилироваться.
Кстати, что за boost::shared_pointer.cpp? Насколько я помню, shared_pointer реализован как header-only.
#include <string>
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<std::string> StringPtr;
StringPtr f() {
return StringPtr(new std::string);
}
int main() {
StringPtr ptr1(new std::string);
StringPtr ptr2 = ptr1;
StringPtr ptr3 = f();
}
Кстати, что за boost::shared_pointer.cpp? Насколько я помню, shared_pointer реализован как header-only.
Установил Debian Squeeze в виртуалку, поставил туда Boost 1.42 и gcc 4.4. Компилируется со стандартными опциями и с -std=c++0x. Установил туда gcc-4.9 из Debian Jessie. Попробовал скомпилировать c -std=c++11, вот тут действительно получилась ошибка. А без -std=c++11 всё компилируется.
Кстати, clang 3.4 из Debian Jessie успешно компилирует этот код против Boost 1.42 как с опцией -std=c++11, так и без неё. Возможно, clang более либерален к коду в некотором смысле. Например, конструирование std::ifstream из std::string в clang компилируется, а в gcc нет. Хорошо это или плохо, я не знаю.
Кстати, clang 3.4 из Debian Jessie успешно компилирует этот код против Boost 1.42 как с опцией -std=c++11, так и без неё. Возможно, clang более либерален к коду в некотором смысле. Например, конструирование std::ifstream из std::string в clang компилируется, а в gcc нет. Хорошо это или плохо, я не знаю.
Да не переживайте Вы так, я сам с gcc 3.4.6 и бустом 1.49 застрял похоже надолго (mmu-less armv4 железка, похоже больше ничего на ней и не взлетит).
Всё-таки это неявно генерируемый конструктор, а не неявный конструктор.
Согласен, было огромное желание подсократить количество текста в ячейках. Я подумаю как лучше представить, или просто небольшую легенду с допущениями или всё же полный вариант. С другой стороны некий каламбур получается, если заменить синонимами: Неявно генерируемый конструктор не генерируется. В любом случае — я вас услышал.
Я запутался.
Есть класс (структура, не важно):
В этом случае неявный move-constructor не создаётся (По схеме: дважды Нет).
Должен ли компилироваться следующий код?
Ответ — всё в порядке — компилируется и вызывается копирующий конструктор:
Почему? Я как-то не правильно понимаю когда вызывается copy ctor? (Copy constructors):
Если же явно попросить компилятор сгенерировать move-конструктор, то уже действительно вызывается move-конструктор:
Если же явно запретить компилятору генерировать move-конструктор:
Тогда уже компилятор ругается!
Что получается:
Если move-конструктор неявно НЕ сгенерирован, то вызывается copy-constructor
Если move-конструктор явно помечен через
Я окончательно запутался. Пошёл смотреть стандарт :(
Есть класс (структура, не важно):
struct Test
{
Test() : s("s") {}
Test(const Test& other) : s(other.s) { puts("Test(const Test& other)"); }
std::string s;
};
В этом случае неявный move-constructor не создаётся (По схеме: дважды Нет).
Должен ли компилироваться следующий код?
int main()
{
Test t1{};
Test t2(std::move(t1));
puts(t1.s.c_str());
puts(t2.s.c_str());
return 0;
}
Ответ — всё в порядке — компилируется и вызывается копирующий конструктор:
Test(const Test& other)
s
s
Почему? Я как-то не правильно понимаю когда вызывается copy ctor? (Copy constructors):
The copy constructor is called whenever an object is initialized from another object of the same type, which includes
initialization, T a = b; or T a(b);, where b is of type T
function argument passing: f(a);, where a is of type T and f is void f(T t)
function return: return a; inside a function such as T f(), where a is of type T, which has no move constructor.
Если же явно попросить компилятор сгенерировать move-конструктор, то уже действительно вызывается move-конструктор:
struct Test
{
Test() : s("s") {}
Test(const Test& other) : s(other.s) { puts("Test(const Test& other)"); }
Test(Test&& other) = default;
std::string s;
};
//...
s
Если же явно запретить компилятору генерировать move-конструктор:
struct Test
{
Test() : s("s") {}
Test(const Test& other) : s(other.s) { puts("Test(const Test& other)"); }
Test(Test&& other) = delete;
std::string s;
};
Тогда уже компилятор ругается!
Что получается:
Test t2(std::move(t1));
Если move-конструктор неявно НЕ сгенерирован, то вызывается copy-constructor
Если move-конструктор явно помечен через
delete
, то copy-constructor НЕ вызываетсяЯ окончательно запутался. Пошёл смотреть стандарт :(
Попробую на пальцах, при помощи аналогии:
Получается, что в первом случае (Foo) int приведётся к char (без ворнингов только потому, что значение известно компилятору и оно помещается в char). Можешь считать отсутствие метода foo(int) как отсутствие move-ctor при присутсвии copy-ctor. Случай с Bar можно рассматривать как аналогию delete для move-ctor. Конечно, пример натянутый, но вроде понятнее становится.
#include <iostream>
using namespace std;
struct Foo
{
void foo(char a)
{
cout << (int)a << endl;
}
};
struct Bar
{
void bar(char a)
{
cout << (int)a << endl;
}
void bar(int a); // да-да, оно только объхявлено
};
int main()
{
char a = 100;
int b = 50;
Foo foo;
Bar bar;
foo.foo(a); // ок, печатает 100
foo.foo(b); // ок, печатает 50
bar.bar(a); // ok, печатает 100
bar.bar(b); // ошибка копиляции
return 0;
}
Получается, что в первом случае (Foo) int приведётся к char (без ворнингов только потому, что значение известно компилятору и оно помещается в char). Можешь считать отсутствие метода foo(int) как отсутствие move-ctor при присутсвии copy-ctor. Случай с Bar можно рассматривать как аналогию delete для move-ctor. Конечно, пример натянутый, но вроде понятнее становится.
Я просто думал, что смогу для себя объяснить это в рамках overload resolution (Вот хорошее видео от Stephan T. Lavavej — Overload Resolution).
Вроде как даже нашёл: 13.3.1.4 Copy-initialization of class by user-defined conversion (n3797)
И 13.3.1 Candidate functions and argument lists:
Но это не то, скорее всего
Вроде как даже нашёл: 13.3.1.4 Copy-initialization of class by user-defined conversion (n3797)
...the candidate functions are selected as follows:
...When initializing a temporary to be bound to the first parameter
of a constructor that takes a reference to possibly cv-qualified T as its first argument, called with a
single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion
functions are also considered…
И 13.3.1 Candidate functions and argument lists:
A defaulted move constructor or assignment operator (12.8) that is defined as deleted is excluded from the
set of candidate functions in all contexts.
Но это не то, скорее всего
На ACCU 2014 была хорошая презентация на эту тему:
Everything You Ever Wanted To Know About Move Semantics. В частности, слайды с 19 по 30.
Everything You Ever Wanted To Know About Move Semantics. В частности, слайды с 19 по 30.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Пополняем шпаргалки по C++: неявно-генерируемые перемещающий конструктор и оператор присваивания