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

Как следует ссылаться на Variable Template на С++ 14 при объявлении в классе?

Например:

class example{
    public:
        template <class T> static constexpr T var = T(1.5);
};

int main(){

    int a = example::var<int>;

    example obj;
    int b = obj.var<int>;

    return 0;
}

GCC создает ошибку для обоих: 'example::var<T>' is not a function template и 'var' is not a member template function

Clang правильно компилирует первый, но создает ошибку для второго: cannot refer to member 'var' in 'example' with '.'

В соответствии со стандартом С++ 14 (ISO/IEC 14882: 2014):

Раздел 14, пункт 1.

Шаблон переменной в области класса представляет собой шаблон статических данных.

Раздел 9.4, абзац 2.

Статический член s класса X может ссылаться на использование выражения с квалифицированным идентификатором X:: s; нет необходимости использовать синтаксис доступа к члену класса (5.2.5) для ссылки на статический член. Статический член может ссылаться на использование синтаксиса доступа к члену класса, и в этом случае оценивается выражение объекта.

Следовательно, IMHO, переменная шаблон в классе (например, шаблон участника статических данных) может упоминаться в обоих направлениях. Может ли это быть ошибкой в ​​компиляторах?

Единственное, что я нашел, чтобы попытаться оправдать такое поведение, - это предложение в разделе 9.4.2, абзац 1:

Статический член данных не является частью подобъектов класса.

Однако оба вышеупомянутых параграфа остаются в силе. Кроме того, я попробовал тот же пример, ссылаясь на другие статические элементы, такие как переменная, функция и шаблон функции, и все они успешно компилируются как в GCC, так и в Clang.

class example{
    public:
        static int constexpr variable = 1;
        void static function(){ return; }
        template <class T> void static function_template(){ return; }
};

int main(){

    example obj;

    int a = obj.variable;
    int b = example::variable;

    obj.function();
    example::function();

    obj.function_template<int>();
    example::function_template<int>();

   return 0;
}

Спасибо в Advance.

Примечание 1: версии компилятора - clang 3.7.0 и gcc 5.2.1.

Примечание 2: требуется ключевое слово static: Переменный шаблон в области класса

Примечание 3: поскольку я хочу инициализировать шаблон переменной, требуется также ключевое слово constexpr, потому что в моем фактическом коде я создам его с помощью float, double и long double (см. С++ 14 Standard (ISO/IEC 14882: 2014), раздел 9.4.2, пункт 3).

Примечание 4: фактические "определения" этих статических элементов данных вне класса (т.е. template <class T> constexpr T example::var;) не нужны в этих примерах. Я тоже пытался, но это не имеет значения.

4b9b3361

Ответ 1

Я скопировал ваш первый код в Visual Studio 2015 (он скомпилирован успешно). Я добавил несколько выходов через std::cout, где я обнаружил, что использование b дает ошибку компилятора: uninitialized local variable 'b' used. a, с другой стороны, был успешно напечатан, когда b не использовался. Таким образом, казалось бы, С++ немного затрудняет доступ к статическим членам шаблона, как вы сказали, требуя, чтобы вы ссылались на его полное, квалифицированное имя.

Возможно, более любопытными являются следующие строки:

std::cout << example::var<int> << "a\n";

Вышеприведенная строка работает, как ожидалось, вывод 1.5 усечен до 1 и 'a' с новой строкой. Ни о чем не писать.

std::cout << obj.var<int> << "b\n";

Теперь здесь, где это становится интересным... Не только вышеприведенная строка не распечатывает значение для obj.var<int>, а 'b'\n никогда не печатается. Я даже тестировал функции std::cout good() fail() и bad(), ни одна из которых не сообщала, что что-то не так (и дальнейшее использование std::cout было успешно выполнено).

Другая странность, которую я обнаружил, заключалась в том, что auto x = obj.var является законным и подходит для поиска, x имеет тип example. Теперь, делая это с помощью глобальной переменной шаблона, возникает ошибка компилятора (как и ожидалось, первая из них):

template<typename T> constexpr T ex = 1.5;
auto x = ex // compiler error: argument list for variable template "ex" is missing

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

class example
{
public:
    template <class T> static constexpr T var = T(1.5);
    template <typename T> static void thing()
    {
        std::cout << var<T> << '\n';          // works
        std::cout << example::var<T> << '\n'; // also works
    }
};

Теперь, насколько стандарт идет, я склонен полагать, что их фразировка немного... педантична. Секции, которые вы указали из стандарта:

нет необходимости использовать синтаксис доступа к члену класса (5.2.5) для ссылки на статический член.

и

Шаблон переменной в области класса представляет собой шаблон статических данных.

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

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

Но ИМО, обращаясь к статическим данным через оператор выбора членов, является плохой практикой, поскольку статические данные и функции фактически не являются частью данного объекта. Доступ к статическим данным так же, как нестатические данные, может привести к тому, что относительно невинно выглядящий код действительно будет ошибочной логикой. Например, если по какой-то причине у вас был неконстантный статический член с именем something, вы могли бы написать example_object.something = 42, не ожидая, что что-либо изменится для всех других экземпляров этого класса во всей вашей программе (те же проблемы, что и глобальные переменные, на самом деле). Из-за этого (и тот факт, что синтаксис доступа к членству, по-видимому, не работает для статических переменных-членов шаблона), я рекомендую всегда использовать разрешение области для доступа/изменения статического содержимого вне класса. example_class::something = 42 намного понятнее, что мы меняем something на все экземпляры example_class. На самом деле, некоторые более современные языки, такие как С#, требуют доступа к статическим данным через имя класса, если вы не находитесь внутри указанного класса.

Учитывая, что несколько компиляторов являются ошибками для разных частей этой маленькой примерной программы, я бы согласился поспорить, что это не очень хорошо отражено в стандарте (и, вероятно, не используется очень часто на практике), а компиляторы просто обрабатывают его по-другому (другая причина, чтобы избежать этого).

TL;DR

По-видимому, в то время как синтаксис выбора элементов работает для статических переменных-членов, он не работает для статических переменных-членов шаблона (хотя компилятор, похоже, не жалуется). Однако синтаксис разрешения разрешения действительно работает, и IMO должно быть предпочтительным в любом случае.