2016-12-21 3 views
17

Например:Как следует ссылаться на Variable Template в C++ 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 '.'

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

Раздел 14, абзац 1.

A variable template at class scope is a static data member template.

Раздел 9.4, Пункт 2.

A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred to using the class member access syntax, in which case the object expression is evaluated.

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

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

A static data member is not part of the subobjects of a class.

Однако две вышеупомянутые пункты остаются в силе. Кроме того, я попробовал тот же пример, ссылаясь на другие статические элементы, такие как переменная, функция и шаблон функции, и все они успешно компилируются как в 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; 
} 

Thanks in Advance.

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

Примечание 2: ключевое слово static требуется: Variable template at class scope

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

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

+1

Какая версия gcc вы используете? g ++ 6.2 ведет себя как clang. Но в отличие от clang, сообщение об ошибке для 'obj.var ' состоит в том, что * "' var' не является функцией шаблона члена * *. Кажется, что первая часть была исправлена ​​с g ++ 5. – Holt

+0

Я использую gcc 5.2.1 и clang 3.7.0 :) –

+1

Clang trunk принимает этот код. –

ответ

2

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

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

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

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

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

Теперь вот где начинается самое интересное ... Не только не печатает Вышеприведенные строки из значения для obj.var<int>, то 'b'\n никогда не печатается либо. Я даже протестировал против 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 
    } 
}; 

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

it is not necessary to use the class member access syntax (5.2.5) to refer to a static member.

и

A variable template at class scope is a static data member template.

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

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

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

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

Т.Л., др

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

+0

Обязательно очистите потоки с помощью 'std :: flush' или' std :: endl'. Это может объяснить, почему вы ничего не видели с помощью 'std :: cout << obj.var <<" b \ n ";'. –

+0

@ FrançoisAndrieux Ну, я тоже сказал, что потом использовал поток (что и удалось, но я думаю, что забыл упомянуть, что он также был показан), поэтому все, что в буфере должно было отображаться вместе с этим материалом – DeMayo

+0

Thanks @DeMayo за то, что нашли время, чтобы проверить эту проблему. Ваш анализ интересен и подтверждает то, что я думал (и вы также говорите): «новые» C++ 14 Variable Templates не очень хорошо не охватываются ни стандартом, ни компиляторами. На самом деле в Стандартном комитете по-прежнему существуют некоторые проблемы с активным ядром в отношении переменных шаблонов: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html Я также согласен с вашим советы по доступу к статическим данным с использованием разрешения области, я просто тестировал стандарт и компиляторы. –

Смежные вопросы