GCC позволяет получить доступ к частному статическому члену

Этот код работает на GCC, но не clang.

class Base
{
    static constexpr int PRIVATE = 1;
};

struct Derived : public Base
{
    template <class T>
    int bar( T & t )
    {
        return PRIVATE;
    }
};

int main()
{
    Derived d;
    int i = 3;
    d.bar(i);
}

Ссылка Godbolt: https://godbolt.org/g/qPJ47p

В случае частной функции-члена GCC правильно обнаруживает попытку доступа к частному члену, если функция шаблона создается, но в противном случае нет. Clang обнаруживает попытку, даже когда функция шаблона никогда не создается.

Однако при использовании частной статической переменной constexpr GCC (до последнего 8.1) не может остановить частный доступ, даже если создается функция шаблона. Кланг правильно (?) Жалуется.

Вопрос: какой из двух компиляторов соответствует стандарту в этой ситуации?

Мне кажется, что GCC не может быть прав, разрешая доступ к частной статической переменной constexpr. Но в то же время это не похоже на слишком сложную проблему, но она не находится в последнем GCC: это делает ее преднамеренной.

Большое спасибо paxdiablo за его ясный и тщательный ответ. Согласно его предложению, я сделал более полный список тестовых примеров и сузил его до static спецификатора, вызывающего проблему для GCC. См. Эту ссылку Godbolt для получения более подробной информации: https://godbolt.org/g/A3zCLk

Comparison of GCC and Clang:

Private member    | GCC         | Clang

static const      | accept      | reject
static constexpr  | accept      | reject
static            | accept      | reject
const             | instantiate | reject
no-specifiers     | instantiate | reject
static function   | instantiate | reject
function          | instantiate | reject

("instantiate" means GCC rejects it upon template instantiation)

Ответы

Ответ 1

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

int bar(int&) {
    return PRIVATE;
}

то он справедливо жалуется:

testprog.cpp: In member function 'int Derived::bar(int&)':
testprog.cpp:3:26: error: 'constexpr const int Base::PRIVATE' is private
     static constexpr int PRIVATE = 1;
                          ^
testprog.cpp:9:16: error: within this context
         return PRIVATE;
                ^

Я бы просто поднял это как ошибку на gcc. Если у них есть другое представление о ее действительности, они сообщают вам об этом.

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

class Base {
    static constexpr int PRIVATE = 42;
};
struct Derived : public Base {
    template <class T> int bar(T) {
        return PRIVATE;
    }
};
int main() {
    Derived d;
    return d.bar(1);
}

Вы также можете указать различные возможности для объявления PRIVATE и их влияния на gcc и clang (сундуки с этого вопроса):

                                gcc       clang
                              --------   --------
static constexpr int          accepted   rejected
static const int              accepted   rejected
const int                     rejected   rejected
int                           rejected   rejected

Вместо использования функции без шаблонов (как указано выше):

int bar(int) {
    return PRIVATE;
}

похоже, приводит к тому, что gcc "ведет себя":

                                gcc       clang
                              --------   --------
static constexpr int          rejected   rejected
static const int              rejected   rejected
const int                     rejected   rejected
int                           rejected   rejected

Итак, если это действительно проблема gcc, я бы подумал, что существует некоторое взаимодействие между static и templates, которое вызывает проблему.

Ответ 2

прошу прощения, это не ответ, а, возможно, связанный вопрос:

//g++ 4.8.4 compiles, g++ 5.4 fix it so it correctly fails to compile.
template<size_t N>
class T{
  public:
    constexpr static size_t m = N;
};

class A{
  constexpr static int a = 10;
};

namespace{
  auto t = T<A::a>::m;
}

int main(){
  cout <<  t << endl;
  return 0;
}

я еще не нашел номер ошибки.