это скрыто

бы приведенные ниже классы не были шаблонами, я мог бы просто иметьx вderived класс. Тем не менее, с кодом ниже, яиметь использованиеthis->x, Почему?

template <typename T>
class base {

protected:
    int x;
};

template <typename T>
class derived : public base<T> {

public:
    int f() { return this->x; }
};

int main() {
    derived<int> d;
    d.f();
    return 0;
}
 KarolaN14 мая 2018 г., 15:53
Это происходит из-за двухфазного поиска имени (который не все компиляторы используют по умолчанию) и зависимых имен. Есть 3 решения этой проблемы, кроме префиксаx с участиемthis->а именно:1) Используйте префиксbase<T>::x, 2) Добавить заявлениеusing base<T>::x, 3) Используйте глобальный переключатель компилятора, который включает разрешающий режим. Плюсы и минусы этих решений описаны вstackoverflow.com/questions/50321788/...
 templatetypedef10 янв. 2011 г., 02:49
Ах, боже. Это как-то связано с поиском имени. Если кто-то не ответит на это в ближайшее время, я посмотрю его и опубликую (сейчас занят).
 Ali10 янв. 2011 г., 02:58
@Ed Swangren: Извините, я пропустил его среди предложенных ответов при публикации этого вопроса. Я долго искал ответ до этого.

Ответы на вопрос(3)

x скрыто во время наследования. Вы можете показать через:

class derived : public base<T> {

public:
    using base<T>::x;             // added "using" statement
    int f() { return x; }
};
 jamesdlin10 янв. 2011 г., 03:49
Этот ответ не объясняетПочему это скрыто

(Оригинальный ответ от 10 января 2011 г.)

Я думаю, что нашел ответ:Проблема GCC: использование члена базового класса, который зависит от аргумента шаблона, Ответ не является специфичным для gcc.

Обновить: В ответ накомментарий Микаэля, изпроект N3337 стандарта C ++ 11:

14.6.2 Зависимые имена [temp.dep]
[...]
3 В определении класса или шаблона класса, если базовый класс зависит от параметра-шаблона, область действия базового класса не проверяется при поиске неквалифицированного имени ни в точке определения шаблона или члена класса, ни во время создания экземпляра класса. шаблон класса или член.

Будь то«потому что стандарт так говорит» считается ответом, я не знаю. Теперь мы можем спросить, почему стандартные мандаты, но какОтличный ответ Стива Джессопа и другие отмечают, что ответ на этот последний вопрос довольно длинный и спорный. К сожалению, когда речь идет о стандарте C ++, зачастую почти невозможно дать краткое и самостоятельное объяснение того, почему стандарт требует чего-либо; это относится и к последнему вопросу.

Решение Вопроса

x зависимое имя, поэтому поиск откладывается до тех пор, пока не будет известен параметр шаблона.

Длинный ответ: когда компилятор видит шаблон, он должен немедленно выполнить определенные проверки, не видя параметра шаблона. Другие откладываются до тех пор, пока параметр не станет известен. Это называется двухфазной компиляцией, и MSVC этого не делает, но это требуется стандартом и реализуется другими основными компиляторами. Если вам нравится, компилятор должен скомпилировать шаблон, как только он его увидит (для какого-то внутреннего представления дерева разбора), и отложить компиляцию до более поздней версии.

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

В C ++ (и C) для разрешения грамматики кода иногда необходимо знать, является ли что-то типом или нет. Например:

#if WANT_POINTER
    typedef int A;
#else
    int A;
#endif
static const int x = 2;
template <typename T> void foo() { A *x = 0; }

если A является типом, который объявляет указатель (без эффекта, кроме как для затенения глобальногоx). Если A - это объект, это умножение (и запрет на перегрузку некоторых операторов недопустимо, присваивая значение r). Если это не так, эта ошибка должна быть диагностированав фазе 1Стандарт определен как ошибкав шаблонеНе в каком-то конкретном случае. Даже если шаблон никогда не создается, если A являетсяint тогда вышеприведенный код неверен и должен быть диагностирован, как если быfoo это был не шаблон, а простая функция.

Теперь стандарт говорит, что имена, которыене зависимые от шаблона параметры должны быть разрешены в фазе 1.A здесь не зависимое имя, оно относится к одному и тому же, независимо от типаT, Поэтому его нужно определить до того, как шаблон будет определен, чтобы его можно было найти и проверить на этапе 1.

T::A было бы имя, которое зависит от T. Мы не можем знать, на этапе 1, является ли это тип или нет. Тип, который в конечном итоге будет использоваться какT в экземпляре, скорее всего, еще даже не определено, и даже если бы это было так, мы не знаем, какой тип (типы) будет использоваться в качестве параметра шаблона. Но мы должны разрешить грамматику, чтобы выполнить наши драгоценные проверки фазы 1 для плохо сформированных шаблонов. Таким образом, в стандарте есть правило для зависимых имен - компилятор должен предполагать, что они не являются типами, если только он не квалифицирован какtypename указать, что онинаходятся типы, или используются в определенных однозначных контекстах. Например вtemplate <typename T> struct Foo : T::A {};, T::A используется в качестве базового класса и, следовательно, однозначно является типом. ЕслиFoo создается с некоторым типом, который имеет член данныхA вместо вложенного типа A это ошибка в коде, выполняющем создание экземпляра (фаза 2), а не ошибка в шаблоне (фаза 1).

Но как насчет шаблона класса с зависимым базовым классом?

template <typename T>
struct Foo : Bar<T> {
    Foo() { A *x = 0; }
};

Является ли зависимое имя или нет? С базовыми классами,Любые имя может появиться в базовом классе. Таким образом, мы могли бы сказать, что A является зависимым именем, и рассматривать его как нетиповое. Это будет иметь нежелательный эффект, чтокаждое имя в Фу зависим, а следовательнокаждый тип используемый в Foo (кроме встроенных типов) должен быть квалифицирован. Внутри Foo вы должны написать:

typename std::string s = "hello, world";

потому чтоstd::string будет зависимым именем и, следовательно, предполагается, что он не является типом, если не указано иное. Ой!

Вторая проблема с разрешением вашего предпочтительного кода (return x;) это даже еслиBar определяется раньшеFoo, а такжеx не является членом в этом определении, кто-то может позже определить специализациюBar для какого-то типаBazтакой, чтоBar<Baz> действительно есть член данныхxи затем создать экземплярFoo<Baz>, Таким образом, в этом случае ваш шаблон будет возвращать элемент данных, а не глобальныйx, Или наоборот, если определение базового шаблонаBar былоxони могут определить специализацию без нее, и ваш шаблон будет искать глобальныйx вернуться вFoo<Baz>, Я думаю, что это было так же удивительно и печально, как и ваша проблема, но этомолча удивительно, в отличие от неожиданной ошибки.

Чтобы избежать этих проблем, в действующем стандарте говорится, что зависимые базовые классы шаблонов классов просто не ищут имена, если только имена не являются зависимыми по какой-либо другой причине. Это останавливает все от зависимости только потому, что это может быть найдено в зависимой базе. Это также имеет нежелательный эффект, который вы видите - вы должны квалифицировать материал из базового класса, или он не найден. Есть три распространенных способа сделатьA зависит:

using Bar<T>::A; в классе -A теперь относится к чему-то вBar<T>следовательно, зависим.Bar<T>::A *x = 0; в точке использования - опять же,A определенно вBar<T>, Это умножение, так какtypename не был использован, так что, возможно, плохой пример, но нам придется подождать до создания экземпляра, чтобы выяснить,operator*(Bar<T>::A, x) возвращает значение Кто знает, может быть, это так ...this->A; в точке использования -A является членом, поэтому, если он не вFoo, он должен быть в базовом классе, опять же стандарт говорит, что это делает его зависимым.

Двухэтапная компиляция сложна и сложна, и вводит некоторые неожиданные требования для дополнительной словесности в вашем коде. Но, скорее, как и демократия, это, пожалуй, худший из возможных способов ведения дел, помимо всех остальных.

Вы можете обоснованно утверждать, что в вашем примере,return x; не имеет смысла, еслиx является вложенным типом в базовом классе, поэтому язык должен (а) сказать, что это зависимое имя и (2) трактовать его как нетип, и ваш код будет работать безthis->, В какой-то степени вы стали жертвой сопутствующего ущерба от решения проблемы, которая не применима в вашем случае, но все еще существует проблема с вашим базовым классом, потенциально представляющим имена под вами, которые являются теневыми глобалами, или отсутствие имен, о которых вы думали они имели, и вместо этого был найден глобальный.

Можно также утверждать, что значение по умолчанию должно быть противоположным для зависимых имен (предполагается, что тип, если не указано иное, чтобы быть объектом), или что значение по умолчанию должно быть более контекстно-зависимым (вstd::string s = "";, std::string может быть прочитан как тип, так как ничто другое не имеет грамматического смысла, даже еслиstd::string *s = 0; неоднозначно). Опять же, я не знаю, как были согласованы правила. Я предполагаю, что количество страниц текста, которое может потребоваться, сведено к минимуму с созданием множества конкретных правил, для которых контексты принимают тип, а какой - нетип.

 jalf10 янв. 2011 г., 04:41
О, хороший подробный ответ. Разъяснил пару вещей, которые я никогда не удосужился посмотреть. :) +1
 Steve Jessop16 июл. 2013 г., 00:21
Я имею в виду, что это ошибка компилятора на одном этапе, а не на другом. Это то же самое значение, что и два абзаца ранее, я просто больше не прописал все это.
 Steve Jessop10 янв. 2011 г., 04:51
@jalf: есть ли такая вещь, как C ++ QTWBFAETYNSYEWTKTAAHMITTBGOW - «Часто задаваемые вопросы, за исключением того, что вы не уверены, что даже хотите знать ответ, и у вас есть более важные вещи, с которыми нужно разобраться»?
 Matthieu M.10 янв. 2011 г., 09:19
необычный ответ, интересно, может ли вопрос вписаться в часто задаваемые вопросы.
 Ionoclast Brigham12 июл. 2013 г., 21:53
Ого, мы можем сказать, энциклопедические?дай пять Однако есть один тонкий момент: «Если Foo создается с некоторым типом, который имеет член данных A вместо вложенного типа A, то это ошибка в коде, выполняющем создание экземпляра (фаза 2), а не ошибка в шаблоне (фаза 1 ) «. Может быть, лучше сказать, что шаблон не поврежден, но это все же может быть причиной неправильного предположения или логической ошибки со стороны автора шаблона. Если помеченный экземпляр был на самом деле предполагаемым вариантом использования, то шаблон был бы неправильным.

Ваш ответ на вопрос