Я просто заметил, что вы ссылались на мою статью .
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 someDog
, после чего он Dog
указывает на точки к объекту Dog
по адресу 42), что Dog
(тот по адресу 42) предлагается изменить свое имя на Max Dog
. Предположим, что он находится по адресу 74, мы присваиваем параметру someDog
значение 74 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.
Подумайте о контрольных параметрах как как псевдонимы для переданной переменной. Когда этот псевдоним назначается, то и переменная, которая была передана.
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
Я помещаю превосходный ответ 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 только в объявлениях шаблонов и определениях, приведенных ниже.
blockquote>
у вас есть квалифицированное имя, которое относится к типу и зависит от параметра шаблона.