Ошибка компиляции при распаковке вариационного пакета в рекурсивной функции [дубликат]

Я просто заметил, что вы ссылались на мою статью .

Java Spec говорит, что все в Java является передачей по значению. В Java нет такой вещи, как «pass-by-reference».

Ключом к пониманию этого является то, что что-то вроде

Dog myDog;

является not собака; это на самом деле указатель для собаки.

Что это значит, когда у вас есть

Dog myDog = new Dog("Rover");
foo(myDog);

, вы по существу передаете адрес созданного объекта Dog к методу foo.

(я говорю, по сути, потому, что указатели Java не являются прямыми адресами, но проще всего их так думать)

Предположим, что объект Dog находится по адресу памяти 42. Это означает, что мы передаем метод 42.

, если метод был определен как

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

let's посмотрите, что происходит.

  • параметр someDog установлен на значение 42
  • в строке «AAA» someDog, после чего он Dog указывает на точки к объекту Dog по адресу 42), что Dog (тот по адресу 42) предлагается изменить свое имя на Max
  • в строке «BBB», создается новый Dog. Предположим, что он находится по адресу 74, мы присваиваем параметру someDog значение 74
  • в строке «CCC». Некоторое число следит за Dog, которое указывает (объект Dog по адресу 74), что Dog (тот, который находится по адресу 74), предлагается изменить его имя на Rowlf
  • , тогда мы вернем

. Теперь давайте подумаем о том, что происходит вне метода: / g28]

Изменено myDog?

Имеется ключ.

Помня, что myDog является указателем , а не фактический Dog, ответ НЕТ. myDog все еще имеет значение 42; он все еще указывает на оригинал Dog (но обратите внимание, что из-за строки «AAA» его имя теперь «Макс» - все тот же Dog, значение myDog не изменилось.)

Совершенно верно для следовать за адресом и изменять то, что находится в конце его; что не изменяет переменную.

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

В C ++, Ada, Pascal и других языках, поддерживающих pass-by-reference, вы можете фактически изменить переданную переменную.

Если у Java была семантика по-ссылке, метод foo, который мы определили выше, изменился бы там, где myDog указывал, когда он назначил someDog в строке BBB.

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

910
задан MSalters 1 June 2016 в 12:28
поделиться

4 ответа

939
ответ дан Alan 26 August 2018 в 07:06
поделиться
77
ответ дан Community 26 August 2018 в 07:06
поделиться
typedef typename Tail::inUnion<U> dummy;

Однако я не уверен, что реализация inUnion верна. Если я правильно понимаю, этот класс не должен быть создан, поэтому вкладка «fail» никогда не будет автоматически терпеть неудачу. Возможно, было бы лучше указать, находится ли тип в объединении или нет с простым булевым значением.

template <typename T, typename TypeList> struct Contains;

template <typename T, typename Head, typename Tail>
struct Contains<T, UnionNode<Head, Tail> >
{
    enum { result = Contains<T, Tail>::result };
};

template <typename T, typename Tail>
struct Contains<T, UnionNode<T, Tail> >
{
    enum { result = true };
};

template <typename T>
struct Contains<T, void>
{
    enum { result = false };
};

PS: Посмотрите на Boost :: Variant

PS2: посмотрите на typelists , особенно в книге Андрея Александреску: Modern C ++ Design

18
ответ дан Luc Touraille 26 August 2018 в 07:06
поделиться

Я помещаю превосходный ответ JLBorges на аналогичный вопрос дословно из cplusplus.com, так как это наиболее краткое объяснение, которое я прочитал по этому вопросу.

] В шаблоне, который мы пишем, есть два типа имен, которые можно использовать - зависимые имена и не зависимые имена. Зависимое имя - это имя, которое зависит от параметра шаблона; неизменяемое имя имеет то же значение, независимо от параметров шаблона.

Например:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

То, что зависит от зависимого имени, может быть чем-то другим для каждого конкретного экземпляра шаблона. Как следствие, шаблоны C ++ подвержены «двухфазному поиску имен». Когда шаблон сначала анализируется (до того, как выполняется какое-либо создание), компилятор просматривает не зависящие имена. Когда происходит конкретное создание шаблона, параметры шаблона известны к тому времени, и компилятор ищет зависимые имена.

На первом этапе анализатор должен знать, является ли зависимое имя именем типа или имени не-типа. По умолчанию зависимым именем считается имя не-типа.

Использовать ключевое слово typename только в объявлениях шаблонов и определениях, приведенных ниже.


у вас есть квалифицированное имя, которое относится к типу и зависит от параметра шаблона.

1
ответ дан Nik-Lz 26 August 2018 в 07:06
поделиться
Другие вопросы по тегам:

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