Учитывая стоимость, эти случаи одинаковы?
// case 1
int a = 5;
// case 2
int a (5);
// case 3
int a;
a = 5
Учитывая стоимость, эти случаи одинаковы?
// case 1
int a = 5;
// case 2
int a (5);
// case 3
int a;
a = 5
Первая и вторая точно такие же, как и инициализация. Третий отличается, так как это присваивание. Эти различия соответствуют стандарту С++. Тем не менее, компилятор может обрабатывать все три одинаковых!
Три синтаксиса разные, несите меня, а я использую определенный пользователем тип вместо int, я вернусь к int позже.
T a(5); // Direct initialization
T b = 5; // Implicit conversion (5->tmp) + copy-initialization
T c; c = 5; // Default initialization + assignment
В первом случае объект a
строится с помощью конструктора, который принимает int
или тип, который может быть неявно преобразован из int
.
struct T {
T( int ); // T a(5) will call this directly
};
Во втором случае временный объект типа T
создается неявным преобразованием из int
, а затем это временное значение используется для копирования конструкции b
. Компилятору разрешено оптимизировать код и выполнять только неявное преобразование вместо конечного объекта (вместо того, чтобы использовать его для создания временного. Но все ограничения должны быть проверены:
class T {
T( T const & );
public:
explicit implicit T( int );
};
int main() {
T b = 5; // Error 1: No implicit conversion from int to T.
// Fix: remove the `explicit` from the constructor
// Error 2: Copy constructor is not accessible
}
Третий случай - это конструкция по умолчанию, за которой следует назначение. Требования к типу заключаются в том, что он может быть сконфигурирован по умолчанию (есть конструктор без аргументов или вообще не существует никакого определяемого пользователем конструктора, и компилятор будет неявно определять его). Тип должен быть назначен из int
или должно быть неявное преобразование из int
в тип U
, который может быть назначен T
. Как вы видите, требования к типу в случаях дерева различаются.
Помимо семантики различных операций, существует и другая важная разница, не все из них могут использоваться во всех контекстах. В частности, в списке инициализации в классе вы не можете использовать версию инициализации с неявным преобразованием + копии, и вы можете иметь только первую половину конструктора по умолчанию +.
// OK // error // ok but different
struct test { struct test { struct test {
T x; T x; T x;
test(int v) : x(v) {} test(int v) : x=5 {} test( int v ) {
x = v;
}
В первом случае атрибут x
непосредственно инициализируется значением v
. Второй случай - синтаксическая ошибка. Третий случай, первый по умолчанию инициализирует, а затем назначает внутри тела конструктора.
Возвращаясь к примеру int
, все требования удовлетворяются типом, поэтому почти нет разницы в коде, который генерирует компилятор для трех случаев, но все же вы не можете использовать версию int b = 5;
внутри списка инициализаторов для инициализации целочисленного атрибута. Более того, если класс имеет атрибут member, который является постоянным целым числом, то вы не можете использовать эквивалент int c; c =5;
(третий столбец выше), поскольку атрибут member становится const
, когда он входит в блок конструктора, то есть x = v;
выше будет пытаться изменить константу, и компилятор будет жаловаться.
Что касается стоимости, которую каждый из них имеет, если они могут быть использованы вообще, они несут ту же стоимость для int
(для любого типа POD), но не для пользовательских типов, которые имеют конструктор по умолчанию, в в случае которого T c; c = 5;
будет нести стоимость строительства по умолчанию, за которой следует стоимость присвоения. В двух других случаях стандарт явно указывает, что компилятору разрешено создавать точный код (после проверки ограничений).
Для первых двух разницы не будет.
Из стандартных документов, 8.5.11,
Форма инициализации (с использованием скобок или =) обычно несущественна, но имеет значение, когда инициализатор или инициализированный объект имеет тип класса; см. ниже. Инициализатор в скобках может быть списком выражений только тогда, когда Объект, инициализированный, имеет тип класса.
Третий - это не инициализация, а назначение.
И учитывая стоимость,
В первых двух случаях вы создаете целое число со значением 5.
В третьем случае вы создаете целое число со значением undefined и заменяете его на 5..
Если вы используете оптимизирующий компилятор, все они будут компилироваться в один и тот же код. Таким образом, все они имеют одинаковую стоимость.
Да, все они оценивают то же самое ассемблерное представление. Вы можете проверить это, например, с GCC, написав фиктивную функцию, а затем создав выход ассемблера: g++ -S file.cxx -o file.s