Стандартно, но оптимизация не предусмотрена.
На самом деле, я считаю, что это связано с оптимизацией, но она все еще полностью стандартна. †
Этот код:
A a = 1;
вызывает преобразователь †† из A
. A
имеет единственный конструктор преобразования A(int i)
, который позволяет неявное преобразование из int
в A
.
Если вы добавите объявление конструктора к explicit
, вы обнаружите, что код не будет компилироваться.
class A
{
public:
explicit A(int i) : m_i(i) // Note "explicit"
{
cout << "constructor\n";
}
A(const A& a)
{
m_i = a.m_i;
cout << "copy constructor\n";
}
private:
int m_i;
};
void TakeA(A a)
{
}
int main()
{
A a = 1; // Doesn't compile
A a(1); // Does compile
TakeA(1); // Doesn't compile
TakeA(A(1)); // Does compile
return 0;
}
† Снова посмотрев на стандарт, я, возможно, изначально ошибался.
8.5 Инициализаторы [dcl.init]
12. Инициализация, которая происходит при передаче аргумента, функция
возврат, выбрасывание исключения (15.1), обработка исключения (15.3),
и заключенные в скобки списки инициализаторов (8.5.1) называются
копия-инициализация и эквивалентна форме
T x = a;
14. Семантика инициализаторов следующая. пункт назначения
тип - это тип объекта или ссылки, которые инициализируются, и
source type - это тип выражения инициализатора. Тип источника
не определяется, когда инициализатор заключен в фигурные скобки или когда
список выражений в скобках.
...
- Если тип назначения является (возможно, cv-квалифицированным) типом класса:
- Если класс является агрегатом (8.5.1), а инициализатор - это список в скобках, см. 8.5.1.
- Если инициализация является прямой инициализацией, или если это инициализация копированием, когда cv-неквалифицированная версия исходного типа является тем же классом, или производным классом класса назначения, рассматриваются конструкторы. Применимые конструкторы перечислены (13.3.1.3), и лучший выбирается через разрешение перегрузки (13.3). Выбранный таким образом конструктор вызывается для инициализации объекта с выражением (ями) инициализатора в качестве аргумента (ов). Если конструктор не применяется или разрешение перегрузки неоднозначно, инициализация неверна.
- В противном случае (т. Е. Для остальных случаев инициализации копирования) определяемые пользователем последовательности преобразования, которые могут преобразовываться из типа источника в тип назначения или (когда используется функция преобразования), в производный класс перечисляются, как описано в 13.3.1.4, и лучший выбирается через разрешение перегрузки (13.3). Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временный тип назначения. Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации, в соответствии с приведенными выше правилами, объекта, который является местом назначения инициализации копирования. В некоторых случаях реализация позволяет исключить копирование, присущее этой прямой инициализации, путем создания промежуточного результата непосредственно в инициализируемый объект ; см. 12.2, 12.8.
...
Так что в каком-то смысле это очень оптимизация. Но я не стал бы беспокоиться об этом, так как это явно разрешено стандартом, и почти каждый компилятор в настоящее время делает elison.
Более подробное описание инициализации см. в этой статье (# 36) . Статья, кажется, согласна с приведенной выше интерпретацией стандарта:
ПРИМЕЧАНИЕ: в последнем случае ("T t3 = u;") компилятор может вызвать оба
определяемое пользователем преобразование (для создания временного объекта) и T-копия
конструктор (чтобы построить t3 из временного), или он мог выбрать
исключить временный и построить T3 непосредственно от вас (что было бы
завершитьэквивалентно "T t3 (u);").С июля 1997 года и в окончательном проекте стандарта широта компилятора для исключения временных объектов была ограничена, но она все еще разрешена для этой оптимизации и для оптимизации возвращаемого значения.
††
ИСО / МЭК 14882: 2003 C ++ Стандартная ссылка
12.3.1 Преобразование с помощью конструктора [class.conv.ctor]
1. Конструктор, объявленный без спецификатора функции explicit
, который можно вызывать с помощью одного параметра, указывает преобразование типа его первого параметра в тип его класса.Такой конструктор называется конвертирующим конструктором. [Пример:
class X {
// ...
public:
X(int);
X(const char*, int =0);
};
void f(X arg)
{
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
}
- конец примера]
2. Явный конструктор создает объекты так же, как не- явные конструкторы, но только в тех случаях, когда синтаксис прямой инициализации (8.5) или где явно используются преобразования (5.2.9, 5.4).Конструктор по умолчанию может быть явным конструктором;такой конструктор будет использоваться для выполнения инициализации по умолчанию или инициализации значения (8.5). [Пример:
class Z {
public:
explicit Z();
explicit Z(int);
// ...
};
Z a; // OK: default-initialization performed
Z a1 = 1; // error: no implicit conversion
Z a3 = Z(1); // OK: direct initialization syntax used
Z a2(1); // OK: direct initialization syntax used
Z* p = new Z(1); // OK: direct initialization syntax used
Z a4 = (Z)1; // OK: explicit cast used
Z a5 = static_cast<Z>(1); // OK: explicit cast used
- конец примера]