Почему здесь не называется конструктор перемещения std :: string? [Дубликат]

Другое событие NullPointerException возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals для гарантированного непустого объекта.

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

Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

4
задан ElefEnt 17 September 2015 в 00:20
поделиться

3 ответа

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

Я утверждаю, что хотя это поведение сначала кажется не имеющим смысла, когда вы считаете его более осторожным, оно имеет смысл и является правильным поведением. Во-первых, следует заметить, что категория значений явно является свойством выражений , , а не самих объектов. Это очевидно, так как std::move никогда не создает новый объект, а просто создает выражение rvalue, относящееся к данному объекту. Тогда мы должны понимать, что:

  • Если выражение является lvalue, оно обычно означает, что значение объекта, на которое ссылается выражение, может или будет доступно через одно и то же выражение позже в той же области , Это предположение по умолчанию, когда к объекту обращаются через именованную переменную.
  • Если выражение представляет собой rvalue, это обычно означает, что значение объекта, на которое ссылается выражение, не может или не будет доступно через то же выражение позже в той же области. (Это включает в себя временные значения prvalue, T{} в одном выражении отличается от T{} в более позднем выражении, оба создают анонимные объекты, но оба они различны, поэтому последний не имеет доступа к тому же объекту, что и первый.)

Таким образом, категория значений выражения, ссылающегося на объект, является relative; зависит от конкретного выражения и области видимости. std::move сигнализирует о своем намерении не получать доступ к значению объекта снова в той же области действия, что позволяет вызываемой функции перемещать свое значение из этого объекта. Однако, когда вызываемая функция обращается к имени ссылки rvalue, это значение является постоянным во время вызова функции; функция может перемещать значение из этого объекта в любой точке или вообще не работать, но, во всяком случае, он, вероятно, получит доступ к нему в теле, который после инициализации параметров.

В этом примере :

void f(Foo&& foo) { /* use foo */ }
void g() {
    Foo foo;
    f(std::move(foo));
}

, хотя std::move(foo) в g и параметр foo в вызываемой ссылке относятся к одному и тому же объекту, это значение объекта исчезнет на точки std::move в g, тогда как в f ожидается, что значение этого объекта будет доступно через foo, возможно, несколько раз до конца f.

A аналогичная ситуация существует при вызове ref-квалифицированных функций-членов.

struct Foo {
    void f() &;
    void f() &&;
    void g() && {
        f(); // calls lvalue-qualified f
    }
};
void h() {
    Foo().g();
}

Здесь значение Foo() вот-вот исчезнет из h(); он не будет доступен после полного выражения . Однако в теле Foo::g() он остается постоянным до конца g(); *this надежно обращается к значению одного и того же объекта. Поэтому естественно, что когда g() вызывает f(), он должен вызывать перегрузку, ожидающую lvalue, и f() не должен красть значение *this* из g(), так как g() может все еще хотеть получить к нему доступ .

4
ответ дан Brian 19 August 2018 в 06:14
поделиться

В g() t - именованная переменная. Все именованные переменные являются lvalues. Если T является типом шаблона, вы можете перенаправить переменную на f() с помощью std::forward . Это вызовет f() с тем же типом, который был передан в g()

template<typename T>
g(T&& t) { f(std::forward<T>(t));}

Если T не тип шаблона, а просто тип, вы можете использовать std::move

g(T&& t) { f(std:move(t)); }
3
ответ дан NathanOliver 19 August 2018 в 06:14
поделиться
  • 1
    Извините, но я не понял. Что такое & quot; не тип шаблона & quot ;? С OR без шаблонов, мой код ideone.com/NL0EDv , похоже, не имеет проблем с использованием std::move, то зачем вообще использовать forward? – Saurav Sahu 22 August 2017 в 15:41

То, что автоматически обрабатывается как rvalues, это вещи без имен, а вещи, которые (очень скоро) не будут иметь имени (в случае возвращаемого значения).

T&& t имеет имя, это t.

Причина, по которой rvalues ​​- это то, что ссылается на них после , что точка использования почти невозможна.

T&& является ссылкой типа rvalue. Ссылка rvalue может связываться только с rvalue (без участия static_cast), но в остальном это значение lvalue типа rvalue.

Тот факт, что он относится к типу rvalue, имеет значение только во время его построения , и если вы сделаете decltype(variable_name). В противном случае это просто другое значение ссылочного типа.

std::move(t) выполняет return static_cast<T&&>(t); и возвращает ссылку rvalue.

Правила, которые регулируют это, написаны стандартными в стандарте C ++. Копия / вставка из них не все так полезно, потому что их не так просто понять.

Первое общее правило: вы получаете неявный ход (aka, параметр, который привязывается к rvalue ссылочный аргумент), когда вы возвращаете именованное значение из функции или когда значение не имеет имени или когда функция явно возвращает ссылку rvalue.

Во-вторых, только ссылки rvalue и const& могут связываться на rvalues.

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

Forth, T&& не всегда является ссылкой rvalue. Если T имеет тип X& или X const&, тогда свертывание ссылки сворачивает T&& в X& или X const&.

Наконец, T&& в контексте вывода типа выводит T в качестве X, X&, X const& или X const&& в зависимости от типа аргумента и, следовательно, может выступать в качестве «ссылки пересылки».

9
ответ дан Yakk - Adam Nevraumont 19 August 2018 в 06:14
поделиться
  • 1
    Что вы подразумеваете под & quot; Ссылка rvalue может привязываться только к rvalue без static_cast & quot ;? – ElefEnt 17 September 2015 в 02:26
  • 2
    @ElefEnt связывается во время строительства – Yakk - Adam Nevraumont 17 September 2015 в 04:03
Другие вопросы по тегам:

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