Подтвердить что ты не робот

Элемент `static constexpr auto`, инициализированный с помощью unnamed перечисления

Я работал над проектом С++ 11 исключительно с помощью clang++-3.4 и решил скомпилировать с помощью g++-4.8.2 в случае возникновения каких-либо несоответствий в полученных ошибках. Оказалось, что g++ отклоняет некоторый код, который принимает clang++. Я уменьшил проблему до MWE, приведенного ниже.


enum { a };

template <class T>
struct foo
{
    static constexpr auto value = a;
};

int main()
{
    static constexpr auto r = foo<int>::value;
}

foo.cpp: 5: 23: ошибка: 'const<anonymous enum> foo<int>::value, объявленная с использованием анонимного типа, используется, но не определена [-fpermissive]

static const auto value = A;

Я бы хотел, чтобы какая-то помощь ответила на следующие два вопроса:

  • Какой компилятор прав в своей интерпретации стандарта? Я предполагаю, что один компилятор прав в том, что он принимает или отклоняет код, а другой ошибочен.

  • Как я могу обойти эту проблему? Я не могу назвать анонимное перечисление, потому что это из сторонней библиотеки (в моем случае перечисления были Eigen::RowMajor и Eigen::ColMajor).

4b9b3361

Ответ 1

Кто виноват?

GCC неточно отклоняет ваш фрагмент, он является законным в соответствии со стандартом С++ 11 (N3337). Котировки с доказательством и объяснением находятся в конце этого сообщения.

обходной путь (A) - добавьте недостающее определение

template <class T>
struct foo {
    static constexpr auto value = a;
    typedef decltype(a) value_type;
};

template<class T>
constexpr typename foo<T>::value_type foo<T>::value;


обходной путь (B) - используйте базовый тип перечисления в качестве заполнителя

#include <type_traits>

template <class T>
struct foo {
  static const std::underlying_type<decltype(a)>::type value = a;
};

Что говорит Стандарт? (N3337)

Как указано, фрагмент - это законный С++ 11, который можно прочитать в следующих цитируемых разделах.


Когда мы можем использовать тип без привязки?

[basic.link]p8 содержит подробные формулировки, которые описывают, когда тип "без привязки", и он указывает, что нумеруемое перечисление считается таким типом.

[basic.link]p8 также явно содержит три контекста, где такой тип нельзя использовать, но ни один из контекстов не применим к нашему использованию, поэтому мы в безопасности.

Тип без привязки не должен использоваться как тип переменной или функции с внешней связью, если

     
  •   
  • объект имеет ссылку на язык C (7.5) или  
  • объект объявляется в неназванном пространстве имен (7.3.1) или  
  • объект не odr-used (3.2) или определен в той же единице перевода  


Вы уверены, что можете использовать auto в таком контексте?

Да, и это может быть подтверждено следующей цитатой:

7.1.6.4p auto спецификатор [dcl.spec.auto]

A auto Тип-спецификатор также может использоваться при объявлении переменной в условии оператора выбора (6.4) или оператора итерации (6.5) в типе-спецификаторе-seq в идентификаторе нового типа или тип-идентификатор нового выражения (5.3.4), в декларации для диапазона и в объявлении статического элемента данных с помощью элемента выравнивания или равенства, который появляется в спецификации элемента определения класса (9.4.2).

Ответ 2

Какой компилятор прав в своей интерпретации стандарта?

gcc неверно. §9.4.2/3:

Статический член данных типа literal может быть объявлен в классе определение с помощью спецификатора constexpr; если это так, то его выражение должно задайте инициализатор скобок или равный, в котором каждый параметр-инициализатор то есть выражение-присваивание является постоянным выражением. Участник все еще должны быть определены в области пространства имен, если он используется (3.2) в формате odr программа и определение области пространства имен не должны содержать инициализатор.

И имя не используется odr в соответствии с §3.2:

Переменная, имя которой отображается как потенциально оцениваемое выражение, odr-used , если это не объект, который удовлетворяет требованиям для отображения в постоянном выражении (5.19) и lvalue-to-rvalue преобразование (4.1) немедленно применяется.

Это действительно так: он удовлетворяет требованиям для появления в постоянном выражении и немедленное применение преобразования lvalue-to-rval (оно используется как инициализатор для объекта). Таким образом, отказ GCC неверен.


Возможным обходным решением является определение члена (но без типа заполнителя). Это определение достаточно для Clang и GCC:

template< typename T >
constexpr decltype(a) foo<T>::value;

Ответ 3

Обходной путь с decltype:

enum { a };

template <class T>
struct foo
{
    static constexpr auto value = a;
};

template <class T>
constexpr decltype(a) foo<T>::value;

int main()
{
    static constexpr auto r = foo<int>::value;
}