Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
template_metaprogramming_ru.docx
Скачиваний:
12
Добавлен:
01.07.2025
Размер:
774 Кб
Скачать
☆

Блок «with»

В дампе VC ++ 6 виден вызов std::copy. Чтобы сделать тот факт более очевидным, мноие компиляторы отделяют фактические аргументы шаблона от имен шаблонных специализаций.

Например, заключительная строка дампа в GCC:

/usr/include/c++/3.2/bits/stl_algobase.h:349: instantiated from

'_OutputIter std::copy(_InputIter, _InputIter, _OutputIter)

[with _InputIter = std::_List_iterator<std::string, std::string&,

std::string*>, _OutputIter = std::back_insert_iterator<std::map<

std::string, std::string, std::less<std::string>, std::allocator<

std::pair<const std::string, std::string> > > >]'

messages continue...

Зарезервированные идентификаторы:

Стандарт C++ резервирует идентификаторы, которые начинаются с подчеркивания и прописной буквы (как _InputIter) и идентификаторы, содержащие двойные подчеркивания (__FUNCTION__) для использования реализацией языка, стандартная библиотека использует эти имена для внутренних реализаций, их явное использование в пользовательном коде может привести к неопределенности.

Так, видно что к сигнатуре std::copy применяется данный подход, что дает информацию о требованиях концепции для передачи аргументов параметрам.

В MCVC 7 был также применен данных подход и введены полезные разрывы строк:

foo.cpp(10) : see reference to function template instantiation

'_OutIt std::copy(_InIt,std::list<_Ty,_Ax>::iterator,

std::back_insert_iterator<_Container>)' being compiled

with

[

_OutIt=std::back_insert_iterator<std::map<std::string,std

::string,std::less<std::string>,std::allocator<std::pair<

const std::string,std::string>>>>,

_InIt=std::list<std::string,std::allocator<std::string>>::iterator,

_Ty=std::string,

_Ax=std::allocator<std::string>,

_Container=std::map<std::string,std::string,std::less<std

::string>,std::allocator<std::pair<const std::string,std::

string>>>

]

К сожалению, при этом, выводится также информация, которая не требуется. Вместо того, чтобы повторить дважды _InIt и _OutIt в сигнатуре функции, второй и третий типы параметров записываются в полном объеме и повторяется в блоке «with».

Устранение параметров шаблона по умолчанию

В версии 7.1 Microsoft исправили это, позволяя видеть, что первые два параметра в std::copy имеют одинаковый тип.

foo.cpp(10) : see reference to function template instantiation '

_OutIt std::copy<std::list<_Ty>::iterator,std::

back_insert_iterator<_Container> >(_InIt,_InIt,_OutIt)' being

compiled

with

[

_OutIt=std::back_insert_iterator<std::map<std::string,std::

string>>,

_Ty=std::string,

_Container=std::map<std::string,std::string>,

_InIt=std::list<std::string>::iterator

]

messages continue...

Если бы выделенный участок выше, заменить на std::copy<_InIt, _OutIt>, то элемент _Ty, можно было бы вынести из блока «with».

Тот факт, что параметр по умолчанию был проигнорирован в дампе – упрощает сообщение.

Глубокий typedef

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

Например:

#include <map>

#include <vector>

#include <algorithm>

int main()

{

std::map<int,int> a;

std::vector<int> v(20);

std::copy(a.begin(), a.end(), v.begin());

return 0;

}

Приводит к следующему дампу в Intel C++ 8.0:

C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\INCLUDE\

xutility(1022): error: no suitable conversion function from "std::

allocator<std::pair<const int, int>>::value_type" to "std::

allocator <std::_Tree<std::_Tmap_traits<int, int, std::less<int>,

std::allocator<std::pair<const int, int>>, false>>::key_type=

{std::_Tmap_traits<int, int, std::less<int>, std::allocator<std::

pair<const int, int>>, false>::key_type={int}}>::value_type=

{std::_Allocator_base<std::_Tree<std::_Tmap_traits<int, int,

std::less<int>, std::allocator<std::pair<const int, int>>,

false>>::key_type={std::_Tmap_traits<int, int, std::less<

int>, std::allocator<std::pair<const int, int>>, false>::

key_type={int}}>::value_type={std::_Tree<std::_Tmap_traits

<int, int, std::less<int>, std::allocator<std::pair<const

int, int>>, false>>::key_type={std::_Tmap_traits<int, int,

std::less<int>, std::allocator<std::pair<const int, int>>,

false>::key_type={int}}}}" exists

*_Dest = *_First;

^

...

Проблема состоит в том, что нельзя выполнить присвоение pair <int, int> (map элемент) в int (элемент vector). В сообщении, этот факт, очень сложно обнаружить. В идеальном случае, сообщение должно было быть таким:

Нельзя преобразовать вложенный элемент value_type из:

allocator<pair<int,int>>

во вложенный элемент value_type из:

_allocator<

_Tree<...>::key_type... (which is some

_Tmap_traits<...>::key_type, which is int)

>

Т.к. второй value_type из

_Allocator_base<

_Tree<...>::key_type... (which is some

_Tmap_traits<...>::key_type, which is int)

>

имеет тип key_type в _Tree <...>, с аргументом типа int.

В результате, вместо того, чтобы просто указать для каких элементов нельзя выполнить преобразование, была выведена информация о том, как эти элементы были получены в реализации стандартной библиотеки.

Вот как выглядит сообщение о той же ошибке в MSVC 7.1:

C:\Program Files \Microsoft Visual Studio .NET 2003\Vc7 \include\

xutility(1022) : error C2440: '=' : cannot convert from 'std::

allocator<_Ty>::value_type' to 'std::allocator<_Ty>::value_type'

with

[

_Ty=std::pair<const int,int>

]

and

[

_Ty=std::_Tree<std::_Tmap_traits<int,int,std::less<

int>,std::allocator<std::pair<const int,int>>,

false>>::key_type

]

...

Это сообщение намного короче, но это не обязательно хорошо: дамп сообщает, что std::allocator<_Ty>::value_type не может быть преобразован к своему собственному типу, а насамом деле имеется в виду разнве типы _Ty в блоках «with» и «and».

Хорошо знать, что известно, что «::value_type» является вложенным элементом в std::allocator и явялется типом переданного аргумента, если бы не этот однообразный подход, по имени value_type нельзя было бы сказать ничего конкретного.

Так как std::allocator<_Ty>::value_type является по существу метафункциональным вызовов, этот вид глубокого перелпределения посредством typedef является важным фактором для отладки метапрограммы.

Пример:

#include <boost/mpl/transform.hpp>

#include <boost/mpl/vector/vector10.hpp>

namespace mpl = boost::mpl;

using namespace mpl::placeholders;

template <class T>

struct returning_ptr

{

typedef T* type();

};

typedef mpl::transform<

mpl::vector5<int&,char,long[5],bool,double>,

returning_ptr<_1>

>::type functions;

Задача заключалась в том, чтобы создать последовательность типов функций, возвращающих указатели на типы во входной последовательности, но автор забыл учесть тот факт, что формирование указателя на ссылочный тип (int&) недопустимо в C++.

C++ Intel 7.1 выдает такой отчет:

foo.cpp(19): error: pointer to reference is not allowed

typedef T* type();

^

detected during:

instantiation of class "returning_ptr<T> [with T=boost::

mpl::bind1<boost::mpl::quote1<returning_ptr>, boost::mpl

::lambda_impl<boost::mpl::_1, boost::mpl::false_>::type>

::apply<boost::mpl::vector_iterator<boost::mpl::vector5<

int &, char, long [5], bool={bool}, double>::type, boost

::mpl::integral_c<long, 0L>>::type, boost::mpl::void_,

boost::mpl::void_, boost::mpl::void_, boost::mpl::void_>

::t1]" at line 23 of "c:/boost/boost/mpl/aux_/has_type.

hpp"

Вцелом сообщение верное, но проблема совершенно в ином. Мы действительно хотели бы знать, какия является тип T в момент ошибки, но он выражается с точки зрения вложенного переопределения типа: mpl::bind1 <...>::t1.

В MSVC 10:

см. ссылку на создание экземпляров класса шаблон при компиляции "returning_ptr<T>"

with

[

T=int &

]

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]