Форум » C/C++ » std::common_type, variadic template и баг MS VC++ 2010 » Ответить

std::common_type, variadic template и баг MS VC++ 2010

Сыроежка: В стандарте C++ 2011 появилась новая библиотека, которая удобна для использования в метапрограммировании с помощью шаблонов, содержащая различные компоненты для работы с типами данных. Объявления этих компонент находятся в заголовочном файле <type_traits>. Одной из таких компонент является следующая шаблонная структура template <class... T> struct common_type; Как видно из ее объявления, она имеет переменное число шаблонных параметров или, иначе говоря, она представляет из себя variadic template. Член данных этой структурфы с именем type представляет собой имя типа, который является "общим" для всех шаблонных аргументов, то есть тот тип, к которому все типовые шаблонные аргументы могут быть приведены. Так как эта компонента имеет переменное число параметров, то общий тип выводится последовательно, сначала выводя общий тип для первых двух аргументов, затем выводится общий тип для полученного на первом шаге общего типа для первых двух аргументов и типа третьего аргумента, и т.д. Вот определение этой компоненты, взятое из стандарта: [quote]3 The nested typedef common_type::type shall be defined as follows: template <class ...T> struct common_type; template <class T> struct common_type<T> { typedef T type; }; template <class T, class U> struct common_type<T, U> { typedef decltype(true ? declval<T>() : declval<U>()) type; }; template <class T, class U, class... V> struct common_type<T, U, V...> { typedef typename common_type<typename common_type<T, U>::type, V...>::type type; };[/quote] Из этого определения видно, как последовательно выводится общий тип для заданных аргументов. То есть это происходит рекурсивно. Об этом не следует забывать, так как порядок следования типов при использовании этой компоненты важен, и перестановка шаблонных аргументов может привести к различным результатам. Чтобы было понятно, рассмотрим простой пример. [pre2]#include <type_traits> #include <iostream> #include <typeinfo> struct Bar { int m; }; struct Foo { int m; }; struct Baz { Baz(const Bar&); Baz(const Foo&); }; int main() { std::cout << typeid( std::common_type<Bar, Baz>::type ).name() << std::endl; std::cout << typeid( std::common_type<Foo, Baz>::type ).name() << std::endl; std::cout << typeid( std::common_type<Baz,Foo, Bar>::type ).name() << std::endl; }[/pre2] В этом примере класс Baz имеет два конструктора преобразования из Bar и Foo в Baz. Если запустить этот пример на выполнение, то общим типом будет тип Baz. Так как сначала рассматривается общий тип для Baz и Foo, которым будет тип Baz, так как тип Foo может быть преобразован к Baz. После этого выводится общий тип для Baz и Bar. И опять-таки общим типом будет Baz по той же самой причине, что и на предыдущем шаге. Но если в последнем предложении программы в конструкции std::common_type переставить местами типы Baz и Bar, то есть если последнее предложение записать в виде [pre2] std::cout << typeid( std::common_type<Bar,Foo, Baz>::type ).name() << std::endl;[/pre2] то уже получится ошибка компиляции, так как нет общего типа для Bar и Foo, то есть ни в одном из этих классов нет функции преобразования из одного типа в другой, и классы не связаны родственными отношениями. Об этой особенности данной компоненты не стоит забывать, чтобы не тратить лишнее время на перекомпиляцию программы в следствии неправильной первоначальной растановки шаблонных аргументов при использовании std::common_type.. Теперь что касается бага компилятора MS VC++ 2010. Этот компилятор не поддерживает variadic template, а потому реализация std::common_type не соответствует стандарту. И, как это обычно бывает, если реализация не соответствует стандарту, то, скорей всего, она дает неверные результаты. В этом легко убедиться, если запустить на выполнение следующий пример: [pre2]#include "stdafx.h" #include <typeinfo> #include <iostream> #include <type_traits> int _tmain(int argc, _TCHAR* argv[]) { unsigned x = 0; long y = 0; std::cout << typeid( ( true ) ? x : y ).name() << std::endl; std::cout << typeid( std::common_type<unsigned, long>::type ).name() << std::endl; return 0; }[/pre2] Результатом работы этого примера должен был бы быть следующий вывод на консоль [quote]unsigned long unsigned long[/quote] То есть имена типов для обоих выражений должны совпадать. Однако действительный вывод этой программы, скомпилированной компилятором MS VC++ 2010 отличается от ожидаемого и дает неверный результат: [quote]unsigned long long[/quote] Это очеивдный баг компилятора MS VC++ 2010. Сообщение о баге я направил в Майкрософт разработчикам компилятора.

Ответов - 1

Сыроежка: Хорошая новость пришла от Майкрософт, сообщающая, что в MS VC++ 2012 RC этот баг устранен. Вот ответ от Майкрософт: Posted by Microsoft on 29.07.2012 at 22:47 Thanks for your feedback. After testing this issue on vs2012. We found both the results are unsigned long. You can refer to the attached picuter. But now we are no longer accepting bugs for Microsoft Visual Studio 2010 and earlier products. we only support the latest Visual Studio versions for bug/suggestion. We are therefore closing this issue. If you can reproduce the issue on VS 2012, please reactivate this issue(Click Edit this item button on the site and change the Status to Active). We are therefore closing this issue. If you can reproduce the issue on VS 2012, please reactivate this issue(Click Edit this item button on the site and change the Status to Active).



полная версия страницы