Видимость члена класса в подписи объявления функции-члена

Почему это работает:

template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};

Но это не так (a а также f поменялись местами):

template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};

говоря это a не объявляется в этой области (внутри decltype), но добавляется явное this-> заставляет это работать.

3

Решение

template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};

Это работает, потому что внутри конечного возвращаемого типа члены окружающего класса видны. Не все члены, а только члены, объявленные до этого (в конце типа возврата класс не считается завершенным, в отличие от функциональных органов). Итак, что здесь сделано:

  • Поскольку мы находимся в шаблоне, выполняется поиск, чтобы увидеть, a зависит или нет. поскольку a было объявлено до f, a найдено, чтобы обратиться к члену класса.
  • По правилам шаблонов в C ++ установлено, что a ссылается на член текущего экземпляра, так как он является членом экземпляров окружающего шаблона. В C ++ это понятие используется главным образом для определения того, являются ли имена зависимыми: если известно, что имя ссылается на члены окружающего шаблона, его не обязательно искать при создании экземпляра, поскольку компилятор уже знает код шаблона (который используется как основание для класса, созданного из него!). Рассматривать:

    template<typename T>
    struct A {
    typedef int type;
    void f() {
    type x;
    A<T>::type y;
    }
    };
    

В C ++ 03 вторая строка объявляется y было бы ошибкой, потому что A<T>::type был зависимым именем и нуждался в typename перед ней. Только первая строка была принята. В C ++ 11 это несоответствие было исправлено, и оба типа имен независимы и не нуждаются в typename, Если вы измените typedef на typedef T type; тогда обе декларации, x а также y будет использовать зависимый тип, но ни понадобится typenameпотому что вы все еще называете элемент текущего экземпляра, и компилятор знает, что вы называете тип.

  • Так a является членом текущего экземпляра. Но это зависит, потому что тип используется для его объявления (A) зависит. Однако это не имеет значения в вашем коде. Независимо от того, зависим или нет, a найден и код действителен.

template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};

В этом коде снова a ищется, чтобы увидеть, является ли он зависимым и / или является ли он членом текущего экземпляра. Но так как мы узнали выше, что члены, объявленные после завершающего возвращаемого типа, не видны, мы не можем найти объявление для a, В C ++, кроме понятия «член текущего экземпляра», есть другое понятие:

  • член неизвестной специализации. Это понятие используется для обозначения случая, когда имя может вместо этого ссылаться на члена класса, который зависит от параметров шаблона. Если бы мы получили доступ B::aтогда a будет членом неизвестной специализации, потому что это неизвестный какие объявления будут видны когда B заменяется при создании экземпляра.

  • Ни член действующего, ни член неизвестной специализации. Это касается всех остальных имен. Ваш случай подходит здесь, потому что известно, что a никогда не может быть членом какого-либо экземпляра, когда это происходит (помните, что поиск имени не может найти a, так как он объявлен после f).

поскольку a не зависит от какого-либо правила, поиск, который не нашел никакого объявления переплет, это означает, что нет другого поиска в экземпляре, который мог бы найти объявление. Независимые имена ищутся во время определения шаблона. Теперь GCC по праву выдает ошибку (но учтите, что, как всегда, неправильно сформированный шаблон не требует немедленной диагностики).


template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(this->a.f(b))
{
}
A a;
};

В этом случае вы добавили this и GCC принято. Имя a что следует this-> снова ищем, может ли он быть членом текущего экземпляра. Но, опять же, из-за видимости члена в конечных типах возврата, объявление не найдено. Следовательно, имя считается не являющимся членом текущего экземпляра. Так как при реализации не существует способа, S может иметь дополнительных членов, которые a может соответствовать (нет базовых классов S которые зависят от параметров шаблона), имя также не является членом неизвестной специализации.

Снова C ++ не имеет правил, чтобы сделать this->a зависимый. Однако он использует this->так имя должен обратитесь к некоторому члену S когда он будет создан! Таким образом, стандарт C ++ говорит

Точно так же, если id-выражение в выражении доступа к члену класса, для которого тип выражения объекта является текущим экземпляром, не ссылается на член текущего экземпляра или член неизвестной специализации, программа является некорректной даже если шаблон, содержащий выражение доступа к элементу, не создан; Диагностика не требуется.

Опять же, для этого кода не требуется никакой диагностики (и GCC фактически не дает его). Id-выражение a в выражении доступа к члену this->a зависел в C ++ 03, потому что правила в этом стандарте были не такими сложными и отлаженными, как в C ++ 11. На мгновение представим, что C ++ 03 decltype и конечные типы возврата. Что бы это значило?

  • Поиск был бы отложен до создания экземпляра, потому что this->a будет зависеть
  • Поиск в экземпляре, скажем, S<SomeClass> потерпит неудачу, потому что this->a не будет найден во время создания экземпляра (как мы уже говорили, конечные типы возврата не видят члены, объявленные позже).

Следовательно, раннее отклонение этого кода на C ++ 11 хорошо и полезно.

4

Другие решения

Тело функции-члена компилируется, как если бы оно было определено после класса. Поэтому все, что объявлено в классе, находится в области действия в этот момент.

Однако объявление функции все еще находится внутри объявления класса и может видеть только имена, предшествующие ему.

template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b)); // error - a is not visible here

A a;
};

template <typename A>
template <typename B>
auto S<A>::f(B b) ->
decltype(a.f(b))
{
return a.f(b);   // a is visible here
}
2

Стандарт гласит (раздел 14.6.2.1):

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

this->a является выражением доступа члена класса, поэтому это правило применяется, и поиск происходит в момент его создания, где S<A> завершено.


Наконец, это совсем не решает вашу проблему, потому что в разделе 5.1.1 говорится:

Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение this является значением типа «указатель на резюме-спецификатор-сл XМежду дополнительным резюме-спецификатор-сл и конец Функция четкости, Член-описатель, или же описатель. Оно не должно появляться перед резюме-спецификатор-сл
и он не должен появляться в объявлении статической функции-члена (хотя ее тип и категория значений определяются внутри статической функции-члена, так как они находятся внутри нестатической функции-члена).

Так что вы не можете использовать this-> здесь, так как это до резюме-спецификатор-сл часть объявления функции.

Подождите, нет, это не так! Раздел 8.4.1 говорит

Декларатор в Функция четкости должен иметь форму

D1 (parameter-declaration-clause) cv-qualifier-seq opt ref-qualifier opt exception-specification opt attribute-specifier-seq opt trailing-return-type opt

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector