Друг, личная функция, шаблон псевдонима и decltype ... правильно ли в Clang отклонить это? - PullRequest
6 голосов
/ 24 апреля 2020

В следующем коде ( GodBolt Link ):

#include <utility>

struct Friend {
    class Inner {
        friend struct Friend;

        int function() { return 0; }
    };

    using DirectResult = decltype(std::declval<Inner>().function());

    template <typename T>
    using IndirectResult = decltype(std::declval<T>().function());
};

int main() {
    Friend::DirectResult direct{};
    Friend::IndirectResult<Friend::Inner> indirect{};
    return direct + indirect;
}

Clang совершенно доволен использованием DirectResult, но будет жаловаться, что IndirectResult пытается получить доступ private функция Inner:

<source>:13:55: error: 'function' is a private member of 'Friend::Inner'    
    using IndirectResult = decltype(std::declval<T>().function());
                                                      ^
<source>:18:13: note: in instantiation of template type alias 'IndirectResult' requested here
    Friend::IndirectResult<Friend::Inner> indirect{};
            ^

Я бы ожидал, что доступ будет в порядке, так как псевдоним шаблона объявлен в классе друга.

Однако в моем опыт Clang, как правило, прав (более чем g cc), когда дело доходит до интерпретации стандарта C ++.

Правильно ли Clang отвергать этот код? И если так, что я отсутствует?

Примечание: g cc 7.x, 8.x и 9.x принимают код.

1 Ответ

4 голосов
/ 02 мая 2020

Это ошибка в Clang. За [class.friend] / 2 :

Объявление класса другом означает, что имена частных и защищенных членов из предоставления класса Дружба может быть доступна в спецификаторах базы и в объявлениях членов класса с подружками.

За [class.mem] , шаблон -declaration может быть декларацией члена , а для [temp.pre] /2.5 объявлением псевдонима может быть декларацией в декларации шаблона . Таким образом, шаблон псевдонимов членов имеет доступ к закрытым и защищенным членам друзей класса.

К счастью, эта ошибка, по-видимому, применяется только к defining-type-id псевдоним декларация ; вы можете обойти это, переместив вычисления в вспомогательный класс (с вложенным type псевдонимом) или, более кратко, в аргумент шаблона по умолчанию :

template <typename T, class U = decltype(std::declval<T>().function())>
using IndirectResult = U;
...