clang vs gcc CRTP: переменная constexpr не может иметь нелитеральный тип

У меня есть класс шаблона CRTP:

template <typename S>
class Base
{
    public:
    constexpr static S NOT_SET{0};
};

struct Derived : public Base<Derived>
{
};

Clang (5.0.0) не принимает это:

5 : <source>:5:24: error: constexpr variable cannot have non-literal type 'const Derived'
    constexpr static S NOT_SET{0};
                       ^
8 : <source>:8:25: note: in instantiation of template class 'Base<Derived>' requested here
struct Derived : public Base<Derived>
                        ^
5 : <source>:5:24: note: incomplete type 'const Derived' is not a literal type
    constexpr static S NOT_SET{0};
                       ^
8 : <source>:8:8: note: definition of 'Derived' is not complete until the closing '}'
struct Derived : public Base<Derived>
       ^
1 error generated.
Compiler exited with result code 1

Но gcc (проверено на 4.9.2 и 6.2) нормально его принимает.

Как это сделать в clang?


person Anton    schedule 05.10.2017    source источник


Ответы (1)


Derived не является полным типом, когда вы пытаетесь использовать его в шаблоне класса Base, поэтому вы не можете использовать его таким образом. Это потому, что тип должен быть полным, чтобы можно было объявить переменную этого типа. Обойти это невозможно.
Подводя итог, тип завершается при закрытии } (и другие исключения, которые не имеют отношения к вашему случаю, например, в его функциях-членах).
Это то, что говорит стандарт ( рабочий проект):

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

Следовательно, clang прав, и ошибка говорит примерно о том же.


Как упоминалось в комментариях, существует способ обойти это. Пока производный тип является (скажем так) constexpr конструируемым, вы можете определить функцию constexpr в базовом классе, которая возвращает вам его не установленную версию (что бы это ни значило ).

person skypjack    schedule 05.10.2017
comment
Спасибо: однако, если я пытаюсь перенести существующую кодовую базу, которая использует эту функцию GCC, в clang, что бы вы посоветовали? - person Anton; 05.10.2017
comment
Пока производный тип поддерживает конструкцию constexpr, вы можете определить функцию constexpr в базовом классе, которая возвращает вам его версию not set (что бы это ни значило). Может ли это работать для вас? - person skypjack; 05.10.2017
comment
Да, это хорошая идея. Спасибо. Я приму ваш ответ, но как вы думаете, вы могли бы поместить свой комментарий и в ответ для потомков? :-) - person Anton; 05.10.2017
comment
@Антон Сделано. Спасибо. - person skypjack; 05.10.2017