Существует ли различие между инициализацией копии и прямой инициализацией?

Нет необходимости в регулярном выражении, если пути, которые вы не хотите, начинаются с http. Просто используйте метод startsWith:

s = "http://example.com/abc.jpg";
if(!s.startsWith("http")){
  // add s to you array of URLs
}
228
задан Rakete1111 6 June 2018 в 21:41
поделиться

6 ответов

Обновление C ++ 17

В C ++ 17 значение A_factory_func () изменилось с создания временного объекта (C ++ <= 14) на просто указание инициализации любого объекта, которым инициализируется это выражение (грубо говоря) в C ++ 17. Эти объекты (называемые «объектами результатов») представляют собой переменные, созданные объявлением (например, a1 ), искусственные объекты, созданные, когда инициализация завершается, и отбрасываются, или если объект необходим для привязки ссылки (например, в A_factory_func (); . В последнем случае объект создается искусственно, что называется «временной материализацией», потому что A_factory_func () не имеет переменной или ссылки, которые в противном случае требуют существования объекта).

В качестве примеров в нашем случае в случае a1 и a2 специальные правила говорят, что в таких объявлениях объект результата инициализатора prvalue того же типа, что и a1 , является переменной ] a1 , и поэтому A_factory_func () напрямую инициализирует объект a1 . Любое промежуточное приведение в функциональном стиле не будет иметь никакого эффекта, потому что A_factory_func (another-prvalue) просто «проходит через» объект результата внешнего prvalue, чтобы быть также объектом результата внутреннего prvalue.


A a1 = A_factory_func();
A a2(A_factory_func());

] Зависит от того, какой тип A_factory_func () возвращает. Я предполагаю, что он возвращает A - тогда он делает то же самое - за исключением того, что если конструктор копирования явный, тогда первый не будет работать. Прочтите 8.6 / 14

double b1 = 0.5;
double b2(0.5);

Это то же самое, потому что это ' sa встроенный тип (здесь это означает не тип класса). Прочтите 8.6 / 14 .

A c1;
A c2 = A();
A c3(A());

Это не то же самое. Первая инициализируется по умолчанию, если A не является POD, и не выполняет никакой инициализации для POD (см. 8.6 / 9 ). Вторая копия инициализируется: Значение инициализирует временное, а затем копирует это значение в c2 (Прочтите 5.2.3 / 2 и 8.6 / 14 ). Это, конечно, потребует неявного конструктора копирования (прочтите 8.6 / 14 и 12.3.1 / 3 и 13.3.1.3/1 ). Третий создает объявление функции для функции c3 , которая возвращает A и принимает указатель функции на функцию, возвращающую A (Прочтите 8.2 ).


Углубление в процесс инициализации Прямая и копирующая инициализация

Хотя они выглядят одинаково и должны делать то же самое, в некоторых случаях эти две формы заметно отличаются. Две формы инициализации - прямая и инициализация копирования:

T t(x);
T t = x;

Существует поведение, которое мы можем приписать каждой из них:

  • Прямая инициализация ведет себя как вызов функции для перегруженной функции: функции, в данном случае, являются конструкторами из T (включая явные ), и аргумент равен x . Разрешение перегрузки найдет наиболее подходящий конструктор и при необходимости выполнит любое необходимое неявное преобразование.
  • Инициализация копирования создает неявную последовательность преобразования: она пытается преобразовать x в объект типа T . (Затем он может скопировать этот объект в инициализируемый объект, поэтому также потребуется конструктор копирования - но это не важно ниже)

Как вы видите, инициализация копии в некотором роде является часть прямой инициализации в отношении возможных неявных преобразований: в то время как прямая инициализация имеет все конструкторы, доступные для вызова, и вдобавок может выполнять любое неявное преобразование, необходимое для сопоставления типов аргументов, инициализация копирования может просто настроить один неявная последовательность преобразования.

Я очень старался и получил следующий код для вывода разного текста для каждой из этих форм , без использования конструкторов от «очевидного» до явного .

#include <iostream>
struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }
};

A::operator B() { std::cout << "<copy> "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output: <direct> <copy>

Как это работает. , и почему он выдает такой результат?

  1. Прямая инициализация

    Сначала не Я ничего не знаю о конверсии. Он просто попытается вызвать конструктор. В этом случае доступен следующий конструктор, который точно соответствует :

     B (A const &)
    

    Для вызова этого конструктора не требуется никакого преобразования, не говоря уже о пользовательском преобразовании (обратите внимание, что здесь также не происходит преобразования с квалификацией const). И так будет называть это прямой инициализацией.

  2. Инициализация копирования

    Как сказано выше, инициализация копирования будет создавать последовательность преобразования, когда a не имеет типа B или не является производным от него (что явно имеет место в данном случае). Таким образом, он будет искать способы выполнить преобразование и найдет следующих кандидатов

     B (A const &)
    оператор B (A &);
    

    Обратите внимание, как я переписал функцию преобразования: Тип параметра отражает тип указателя this , который в неконстантной функции-члене является неконстантным. Теперь мы вызываем этих кандидатов с аргументом x . Выигрывает функция преобразования: потому что, если у нас есть две функции-кандидаты, обе принимающие ссылку на один и тот же тип, то выигрывает версия less const (это, кстати, также механизм, который предпочитает не- Функция-член const вызывает неконстантные объекты).

    Обратите внимание, что если мы изменим функцию преобразования на функцию-член const, тогда преобразование будет неоднозначным (поскольку оба имеют тип параметра A const & then): компилятор Comeau отклоняет его правильно, но GCC принимает это в непедантичном режиме. Однако переключение на -педантический заставляет выводить и соответствующее предупреждение о двусмысленности.

Я надеюсь, что это немного поможет прояснить, чем отличаются эти две формы!

242
ответ дан 23 November 2019 в 03:47
поделиться

Это с Языка Программирования на C++ Bjarne Stroustrup:

инициализацию с = рассматривают инициализация копии . В принципе копия инициализатора (объект мы копируем с) помещается в инициализированный объект. Однако такая копия может быть оптимизирована далеко (игнорируемая), и операция пересылки (на основе семантики перемещения) может использоваться, если инициализатор является rvalue. Игнорирование = делает инициализацию явной. Явная инициализация известна как прямая инициализация .

0
ответ дан 23 November 2019 в 03:47
поделиться

Назначение отличается от инициализации .

Обе следующие строки выполняют инициализацию . Выполняется единственный вызов конструктора:

A a1 = A_factory_func();  // calls copy constructor
A a1(A_factory_func());   // calls copy constructor

, но он не эквивалентен:

A a1;                     // calls default constructor
a1 = A_factory_func();    // (assignment) calls operator =

В настоящий момент у меня нет текста, чтобы доказать это, но очень легко поэкспериментировать:

#include <iostream>
using namespace std;

class A {
public:
    A() { 
        cout << "default constructor" << endl;
    }

    A(const A& x) { 
        cout << "copy constructor" << endl;
    }

    const A& operator = (const A& x) {
        cout << "operator =" << endl;
        return *this;
    }
};

int main() {
    A a;       // default constructor
    A b(a);    // copy constructor
    A c = a;   // copy constructor
    c = b;     // operator =
    return 0;
}
46
ответ дан 23 November 2019 в 03:47
поделиться

double b1 = 0.5; - неявный вызов конструктора.

double b2 (0.5); - явный вызов.

Посмотрите на следующий код, чтобы увидеть разницу:

#include <iostream>
class sss { 
public: 
  explicit sss( int ) 
  { 
    std::cout << "int" << std::endl;
  };
  sss( double ) 
  {
    std::cout << "double" << std::endl;
  };
};

int main() 
{ 
  sss ddd( 7 ); // calls int constructor 
  sss xxx = 7;  // calls double constructor 
  return 0;
}

Если в вашем классе нет явных конструкторов, то явные и неявные вызовы идентичны.

17
ответ дан 23 November 2019 в 03:47
поделиться

Первая группировка: это зависит от того, что возвращает A_factory_func . Первая строка - это пример инициализации копии , вторая строка - прямая инициализация . Если A_factory_func возвращает объект A , тогда они эквивалентны, они оба вызывают конструктор копирования для A , в противном случае первая версия создает rvalue типа A из доступных операторов преобразования для типа возвращаемого значения A_factory_func или соответствующих конструкторов A , а затем вызывает конструктор копирования для создания a1 из этого временного . Вторая версия пытается найти подходящий конструктор, который принимает все, что возвращает A_factory_func , или это требует чего-то, во что может быть неявно преобразовано возвращаемое значение.

Вторая группировка: выполняется точно такая же логика, за исключением того, что встроенные типы не имеют никаких экзотических конструкторов, поэтому на практике они идентичны.

Третья группировка: c1 инициализируется по умолчанию, c2 инициализируется копированием из значения, инициализированного временно. Любые члены c1 , которые имеют pod-type (или члены членов и т. Д., И т. Д.), Не могут быть инициализированы, если предоставленные пользователем конструкторы по умолчанию (если есть) не инициализируют их явно. Для c2 это зависит от того, существует ли предоставленный пользователем конструктор копирования и правильно ли он инициализирует эти элементы, но все члены временного элемента будут инициализированы (инициализированы нулем, если не инициализированы явно иначе). Как было замечено, c3 - ловушка. На самом деле это объявление функции.

3
ответ дан 23 November 2019 в 03:47
поделиться

Многие из этих случаев зависят от реализации объекта, поэтому трудно дать вам конкретный ответ.

Рассмотрим случай

A a = 5;
A a(5);

В этом случае предполагается правильный оператор присваивания и инициализация конструктор, который принимает один целочисленный аргумент, то, как я реализую указанные методы, влияет на поведение каждой строки. Однако это обычная практика, когда один из них вызывает другой в реализации, чтобы устранить дублирующийся код (хотя в таком простом случае не было бы реальной цели.)

Изменить: Как упоминалось в других ответах, первая строка фактически вызовет конструктор копирования. Рассматривайте комментарии, относящиеся к оператору присваивания, как к поведению, относящемуся к автономному присваиванию.

Тем не менее, то, как компилятор оптимизирует код, будет иметь собственное влияние. Если у меня есть инициализирующий конструктор, вызывающий оператор «=» - если компилятор не выполняет оптимизацию, верхняя строка затем выполнит 2 перехода, а не один в нижней строке.

Теперь, для наиболее распространенных ситуаций, ваш компилятор оптимизирует эти случаи и устранит этот тип неэффективности. Таким образом, все описанные вами ситуации окажутся одинаковыми. Если вы хотите точно увидеть, что делается, вы можете посмотреть объектный код или вывод сборки вашего компилятора.

Таким образом, все описанные вами ситуации окажутся одинаковыми. Если вы хотите точно увидеть, что делается, вы можете посмотреть объектный код или вывод сборки вашего компилятора.

Таким образом, все описанные вами ситуации окажутся одинаковыми. Если вы хотите точно увидеть, что делается, вы можете посмотреть объектный код или вывод сборки вашего компилятора.

0
ответ дан 23 November 2019 в 03:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: