Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

The Boost Tuple Library

Boost , ,

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

C++ Boost

The Boost Tuple Library

Кортеж (илиn— кортеж) представляет собой набор элементов фиксированного размера. Пары, тройные, четверные и т.д. являются парными. На языке программирования кортеж — это объект данных, содержащий в качестве элементов другие объекты. Эти объекты элементов могут быть разных типов.

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

Некоторые языки программирования, такие как ML, Python и Haskell, имеют встроенные конструкции. К сожалению, C++ этого не делает. Чтобы компенсировать этот недостаток, библиотека Boost Tuple реализует конструкцию с использованием шаблонов.

Table of Contents

  1. Использование библиотеки
  2. Типы труб
  3. Строительные трубы
  4. Доступ к элементам трубки
  5. Копии строительства и комплектации
  6. Операторы связи
  7. Уровни
  8. Стриминг
  9. Выступление
  10. Портативность
  11. Признание
  12. Ссылки

More details

Расширенные возможности(описывает некоторые метафункции и т.д.).

Обоснование некоторых решений по проектированию/реализации.

Using the library

Чтобы использовать библиотеку, просто включите:

#include "boost/tuple/tuple.hpp"

Операторы сравнения могут быть включены в:

#include "boost/tuple/tuple_comparison.hpp"

Использование операторов ввода и вывода,

#include "boost/tuple/tuple_io.hpp"

При этом<tuple_io.hpp>и<tuple_comparison.hpp><tuple.hpp>.

Все определения находятся в пространстве имен<::boost::tuples>, но наиболее распространенные имена поднимаются до пространства имен<::boost>с использованием деклараций. Эти имена:<tuple>,<make_tuple>,<tie>и<get>. Кроме того,<ref>и<cref>определены непосредственно под<::boost>пространством имен.

Tuple types

Тип кортежа — это инстанциация шаблона<tuple>. Параметры шаблона определяют типы элементов кортежа. Нынешняя версия поддерживает кортежи с 0-10 элементами. При необходимости верхний предел может быть увеличен до, скажем, нескольких десятков элементов. Элементом данных может быть любой тип C++. Обратите внимание, что<void>и простые типы функций являются допустимыми типами C++, но объекты таких типов не могут существовать. Следовательно, если тип кортежа содержит такие типы, как элементы, то тип кортежа может существовать, но не является объектом этого типа. Существуют естественные ограничения для типов элементов, которые не могут быть скопированы или которые не могут быть построены по умолчанию (см. ниже «Строительные трубы»).

Например, следующие определения являются действительными инстанциациями (<A>,<B>и<C>являются некоторыми классами, определенными пользователем):

tuple<int>
tuple<double&, const double&, const double, double*, const double*>
tuple<A, int(*)(char, int), B(A::*)(C&), C>
tuple<std::string, std::pair<A, B> >
tuple<A*, tuple<const A*, const B&, C>, bool, void*>

Constructing tuples

Конструктор кортежа принимает элементы кортежа в качестве аргументов. Дляn-элементного кортежа конструктор может быть вызван аргументамиk, где 0<=k<=n. Например:

tuple<int, double>() 
tuple<int, double>(1) 
tuple<int, double>(1, 3.14)

Если начальное значение для элемента не предусмотрено, он инициализируется по умолчанию (и, следовательно, должен быть инициализируемым по умолчанию). Например.

class X {
  X(); 
public:
  X(std::string);
};
tuple<X,X,X>()                                              // error: no default constructor for X
tuple<X,X,X>(string("Jaba"), string("Daba"), string("Duu")) // ok

В частности, типы ссылок не имеют инициализации по умолчанию:

tuple<double&>()                // error: reference must be 
                                // initialized explicitly
double d = 5; 
tuple<double&>(d)               // ok
tuple<double&>(d+3.14)          // error: cannot initialize 
                                // non-const reference with a temporary
tuple<const double&>(d+3.14)    // ok, but dangerous: 
                                // the element becomes a dangling reference 

Использование начального значения для элемента, который не может быть скопирован, является ошибкой времени компиляции:

class Y { 
  Y(const Y&); 
public:
  Y();
};
char a[10];
tuple<char[10], Y>(a, Y()); // error, neither arrays nor Y can be copied
tuple<char[10], Y>();       // ok

Обратите внимание, что следующее совершенно нормально:

Y y;
tuple<char(&)[10], Y&>(a, y); 

Можно придумать тип кортежа, который не может быть построен. Это происходит, если элемент, который не может быть инициализирован, имеет более низкий индекс, чем элемент, который требует инициализации. Например:<tuple<char[10], int&>>.

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

The make_tuple function

Трубы также могут быть построены с использованием вспомогательных функций<make_tuple>(ср.<std::make_pair>). Это делает конструкцию более удобной, избавляя программиста от явного указания типов элементов:

tuple<int, int, double> add_multiply_divide(int a, int b) {
  return make_tuple(a+b, a*b, double(a)/double(b));
}

По умолчанию типы элементов выводятся в простые нессылочные типы. Например:

void foo(const A& a, B& b) { 
  ...
  make_tuple(a, b);

<make_tuple>— [<tuple<A, B>>], [<tuple<A, B>>], [[<tuple<A, B>>].

Иногда простой нессылочный тип нежелателен, например, если тип элемента не может быть скопирован. Таким образом, программист может управлять выводом типа и утверждать, что вместо этого в качестве типа элемента следует использовать ссылку на const или ссылку на неconst. Это достигается с помощью двух вспомогательных шаблонных функций:<ref>и<cref>. Любой аргумент можно обернуть этими функциями, чтобы получить нужный тип. Механизм не компрометирует правильность const, поскольку объект const, завернутый<ref>, приводит к контурному элементу с эталонным типом const (см. пятый пример ниже). Например:

A a; B b; const A ca = a;
make_tuple(cref(a), b);      // creates tuple<const A&, B>
make_tuple(ref(a), b);       // creates tuple<A&, B>
make_tuple(ref(a), cref(b)); // creates tuple<A&, const B&>
make_tuple(cref(ca));        // creates tuple<const A&>
make_tuple(ref(ca));         // creates tuple<const A&>

Аргументы массива к<make_tuple>функциям выводятся для ссылки на типы const по умолчанию; нет необходимости заворачивать их<cref>. Например:

make_tuple("Donald", "Daisy");

Это создает объект типа<tuple<const char (&)[7], const char (&)[6]>>(обратите внимание, что тип строки буквально представляет собой массив конст-символов, а не<const char*>). Однако, чтобы получить<make_tuple>, чтобы создать кортеж с элементом неконст-массивного типа, необходимо использовать обертку<ref>.

Функциональные указатели выводятся на простой нессылочный тип, то есть на простой функциональный указатель. Кортеж может также содержать ссылку на функцию, но такой кортеж не может быть сконструирован с<make_tuple>(результатом будет тип с высокой квалификацией функции, что является незаконным):

void f(int i);
  ...
make_tuple(&f); // tuple<void (*)(int)>
  ...
tuple<tuple<void (&)(int)> > a(f) // ok
make_tuple(f);                    // not ok

Accessing tuple elements

Доступ к элементам трубки осуществляется с помощью выражения:

t.get<N>()

или

get<N>(t)

где<t>является кортежным объектом, а<N>представляет собой постоянное интегральное выражение, определяющее индекс элемента, к которому необходимо получить доступ. В зависимости от того, является ли<t>const или нет,<get>возвращает<N>элемент в качестве ссылки на тип const или non-const. Индекс первого элемента равен 0 и, таким образом,< N>должен быть между 0 и<k-1>, где<k>- число элементов в кортеже. Нарушения этих ограничений обнаруживаются во время компиляции. Примеры:

double d = 2.7; A a;
tuple<int, double&, const A&> t(1, d, a);
const tuple<int, double&, const A&> ct = t;
  ...
int i = get<0>(t); i = t.get<0>();        // ok
int j = get<0>(ct);                       // ok
get<0>(t) = 5;                            // ok 
get<0>(ct) = 5;                           // error, can't assign to const 
  ...
double e = get<1>(t); // ok   
get<1>(t) = 3.14;     // ok 
get<2>(t) = A();      // error, can't assign to const 
A aa = get<3>(t);     // error: index out of bounds 
  ...
++get<0>(t);  // ok, can be used as any variable

Записка! Функции не поддерживаются компилятором MS Visual C++. Кроме того, у компилятора возникают проблемы с поиском функций без явного квалификатора пространства имен. Следовательно, все вызовы<get>должны быть квалифицированы как:<tuples::get<N>(a_tuple)>при написании кода, который должен компилироваться с MSVC++. 6.0.

Copy construction and tuple assignment

Кортеж может быть сконструирован из другого кортежа, при условии, что типы элементов являются сконструируемыми копиями. Аналогичным образом, кортеж может быть назначен другому кортежу, при условии, что типы элементов можно назначать по принципу элемента. Например:

class A {};
class B : public A {};
struct C { C(); C(const B&); };
struct D { operator C() const; };
tuple<char, B*, B, D> t;
  ...
tuple<int, A*, C, C> a(t); // ok 
a = t;                     // ok 

В обоих случаях выполнены преобразования:<char ->int>,<B* ->A*>(полученный указатель класса в указатель базового класса),<B ->C>(конверсия, определенная пользователем) и<D ->C>(конверсия, определенная пользователем).

Обратите внимание, что назначение также определяется из<std::pair>типов:

tuple<float, int> a = std::make_pair(1, 'a');

Relational operators

Трубки сводят операторов<==, !=, <, >, <=>и<>=>к соответствующим элементарным операторам. Это означает, что если любой из этих операторов определен между всеми элементами двух кортежей, то один и тот же оператор также определен между ними. Операторы равенства для двух кортежей<a>и<b>определяются как:

  • a == bДля каждогоi:ai== bi
  • a != bСуществуетi:ai!= bi

Операторы<<, >, <=>и<>=>осуществляют лексикографический заказ.

Обратите внимание, что попытка сравнить два кортежа разной длины приводит к ошибке компиляции времени. Также операторы сравнения являются«короткозамкнутыми»: элементарные сравнения начинаются с первых элементов и выполняются только до тех пор, пока результат не будет ясен.

Примеры:

tuple<std::string, int, A> t1(std::string("same?"), 2, A());
tuple<std::string, long, A> t2(std::string("same?"), 2, A());
tuple<std::string, long, A> t3(std::string("different"), 3, A());
bool operator==(A, A) { std::cout << "All the same to me..."; return true; }
t1 == t2; 		// true
t1 == t3;               // false, does not print "All the..."

Tiers

Уровниявляются кортежами, где все элементы имеют неконстовые эталонные типы. Они построены с призывом к шаблону функции<tie>(см.<make_tuple>):

int i; char c; double d; 
  ...
tie(i, c, a);

Вышеприведенная<tie>функция создает кортеж типа<tuple<int&, char&, double&>>. Такой же результат может быть достигнут и с призывом<make_tuple(ref(i), ref(c), ref(a))>.

Кортеж, содержащий неконстовые ссылки в качестве элементов, можно использовать для «распаковки» другого кортежа в переменные. Например:

int i; char c; double d; 
tie(i, c, d) = make_tuple(1,'a', 5.5);
std::cout << i << " " <<  c << " " << d;

Этот код печатает<1 a 5.5>на стандартный выходной поток. Операция распаковки кортежа, подобная этой, встречается, например, в ML и Python. Это удобно при вызове функций, которые возвращают кортежи.

Механизм связывания также работает с шаблонами<std::pair>:

int i; char c;
tie(i, c) = std::make_pair(1, 'a');

Ignore

Существует также объект<ignore>, который позволяет игнорировать элемент, назначенный кортежем. Идея заключается в том, что функция может вернуть кортеж, только часть которого вас интересует. Например,<ignore>находится под<tuples>подназванием пространства:

char c;
tie(tuples::ignore, c) = std::make_pair(1, 'a');

Streaming

Глобальная<operator<<>была перегружена для<std::ostream>так, что кортежи выводятся путем рекурсивного вызова<operator<<>для каждого элемента.

Аналогичным образом, глобальный<operator>>>был перегружен для извлечения кортежей из<std::istream>путем рекурсивного вызова<operator>>>для каждого элемента.

Делимитером по умолчанию между элементами является пространство, а кортеж заключен в скобки. Например:<

tuple<float, int, std::string> a(1.0f,  2, std::string("Howdy folks!");
cout << a; 
>

выводит кортеж как:<(1.0 2 Howdy folks!)>

Библиотека определяет триманипуляторадля изменения поведения по умолчанию:

  • set_open(char)определяет символ, который выводится перед первым элементом.
  • set_close(char)определяет символ, который выводится после последнего элемента.
  • set_delimiter(char)определяет характер разграничителя между элементами.

Обратите внимание, что эти манипуляторы определены в подименном пространстве<tuples>. Например:

cout << tuples::set_open('[') << tuples::set_close(']') << tuples::set_delimiter(',') << a; 

<a>выдает тот же кортеж, что и:<[1.0,2,Howdy folks!]>

Те же манипуляторы работают и с<operator>>>и<istream>. Предположим, что поток<cin>содержит следующие данные:<

(1 2 3) [4:5]
>

Код:

tuple<int, int, int> i;
tuple<int, int> j;
cin >> i;
cin >> tuples::set_open('[') >> tuples::set_close(']') >> tuples::set_delimiter(':');
cin >> j;

Считывает данные в кортежи<i>и<j>.

Обратите внимание, что извлечение кортежей с элементами струн<std::string>или C-стиля обычно не работает, поскольку представление потокового кортежа не может быть однозначно разборчивым.

Performance

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

class hand_made_tuple { 
  A a; B b; C c;
public:
  hand_made_tuple(const A& aa, const B& bb, const C& cc) 
    : a(aa), b(bb), c(cc) {};
  A& getA() { return a; };
  B& getB() { return b; };
  C& getC() { return c; };
};
hand_made_tuple hmt(A(), B(), C()); 
hmt.getA(); hmt.getB(); hmt.getC();

и этот код:

tuple<A, B, C> t(A(), B(), C());
t.get<0>(); t.get<1>(); t.get<2>(); 

Обратите внимание, что существуют широко используемые компиляторы (например, bcc 5.5.1), которые не могут оптимизировать этот тип использования кортежа.

В зависимости от оптимизирующей способности компилятора, ярусный механизм может иметь небольшой штраф за производительность по сравнению с использованием неконстовых эталонных параметров в качестве механизма для возвращения нескольких значений из функции. Например, предположим, что следующие функции<f1>и<f2>имеют эквивалентные функции:

void f1(int&, double&);
tuple<int, double> f2();

Тогда вызов #1 может быть немного быстрее, чем #2 в коде ниже:

int i; double d;
  ...
f1(i,d);         // #1
tie(i,d) = f2(); // #2

См. [1,2] для более углубленного обсуждения эффективности.

Effect on Compile Time

Составление кортежей может быть медленным из-за чрезмерного количества шаблонных инстанциаций. В зависимости от компилятора и длины кортежа, компиляция конструкции кортежа может быть более чем в 10 раз медленнее по сравнению с компиляцией эквивалентного явно написанного класса, такого как класс<hand_made_tuple>выше. Однако, поскольку реалистичная программа, вероятно, будет содержать много кода в дополнение к набору определений, разница, вероятно, незаметна. Увеличение времени компиляции между 5 и 10 процентами было измерено для программ, которые очень часто использовали кортежи. С помощью тех же тестовых программ потребление памяти для компиляции увеличилось на 22-27%. См.1,2для подробностей.

Portability

Библиотечный код является (?) стандартным C++ и, таким образом, библиотека работает со стандартным компилятором. Ниже приведен список компиляторов и известных проблем с каждым компилятором:

КомпиляторПроблемы
GCC 2.95-
edg 2.44-
Борланд 5.5Не может использовать указатели функций или указатели членов в качестве элементов набора
Метроверки 6.2Нельзя использовать<ref>и<cref>обертки
MS Visual C++Никаких эталонных элементов<tie>до сих пор не работает. Не может использовать<ref>и<cref>обертки

Acknowledgements

Гэри Пауэлл был незаменимой рукой помощи. В частности, его идеей были потоковые манипуляторы для труб. Даг Грегор придумал рабочую версию для MSVC, Дэвид Абрахамс нашел способ избавиться от большинства ограничений для компиляторов, не поддерживающих частичную специализацию. Спасибо Джереми Сике, Уильяму Кемпфу и Йенсу Мауреру за помощь и предложения. Комментарии Весы Карвонен, Джона Макса Скаллера, Эда Брея, Бемана Доуса, Дэвида Абрахамса и Хартмута Кайзера помогли улучшить библиотеку. Идея механизма галстука возникла из старой статьи Иэна Маккаллоха, где он предложил нечто подобное для std::pairs.

References

Järvi J.:Трубочки и множественные значения возврата в C++, TUCS Technical Report No 249, 1999.. [ORIG_END] -->

Järvi J.:ML-Style Tuple Assignment in Standard C++ - Extending the Multiple Return Value Formalism, TUCS Technical Report No 267, 1999.. [ORIG_END] -->

Järvi J.:Трубные типы и множественные возвращаемые значения, C/C++ Users Journal, август 2001.


Последний модифицированный 2003-09-07

© CopyrightJaakko Järvi2001. Разрешение на копирование, использование, изменение, продажу и распространение этого программного обеспечения и его документации предоставляется при условии, что это уведомление об авторских правах появляется во всех копиях. Это программное обеспечение и его документация предоставляются «как есть» без явной или подразумеваемой гарантии и без претензий на его пригодность для любых целей.

Статья The Boost Tuple Library раздела может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 16:43:35/0.013596057891846/1