2011-08-25 3 views
4

Я сейчас читаю эту книгу, шаблоны на C++: Полное руководство. На пункте я застрял не мог понять терминологию, здесь есть пункт:Некоторые путаницы в шаблонах C++

Основной принцип заключается в том, что любой аргумент шаблона должен быть количество или значение, которое может быть определено во время компиляции. Как становится ясно позже, это требование приводит к существенным преимуществам для затрат времени на создание шаблонных объектов. Поскольку параметры шаблона в конечном итоге заменяются значениями времени компиляции, они могут сами использовать для формирования выражений времени компиляции. Это было использовано в шаблоне ArrayInClass , чтобы определить массив массивов-членов. Размер массива должен быть так называемым константным выражением, а параметр N квалифицируется как таковой.

Мы можем выдвинуть это рассуждение немного дальше: Поскольку шаблон параметров во время компиляции сущность, они также могут быть использованы для создания действительных аргументов шаблона. Ниже приведен пример:

template <typename T> 
class Dozen { 
    public: 
    ArrayInClass<T,12> contents; 
}; 

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

Я ничего не могу понять. Я очень ценю любую помощь с простыми и понятными словами.

Edit:

Arrayinclass:

template <typename T, int N> 
class ArrayInClass { 
    public: 
    T array[N]; 
}; 
+0

Какая конкретная часть этого вы не понимаете? Вы понимаете фрагмент кода? –

+0

@ Oli, я понимаю код, но не могу понять смелый параграф. –

+0

Можете ли вы сузить то, что вы не понимаете? это большая и сложная тема. – Kevin

ответ

7

Есть некоторое выражение в C++, которые должны быть известно во время компиляции. Например:

int someArray[30]; 

30должен быть константой во время компиляции в C++ Это могло бы быть:.

int someArray[30 + 3]; 

Это прекрасно, потому что у компилятора есть вся информация, необходимая для вычисления во время компиляции, как большой массив. Однако:

void MyFunc(int numItems) { 
    int someArray[30 + numItems]; 
} 

Это константа не время компиляции, так как пользователь может позвонить MyFunc с любым целочисленным значением. Компилятор не знает, как большой размер массива, так что это ошибка компилятора.

(примечание: C99 допускает создание массивов произвольных размеров, таких как C++).

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

template<int ArrayLen> void MyFunc() { 
    int someArray[30 + ArrayLen]; 
} 

Это юридического C++, потому что каждое использование MyFunc сусла укажите целое число времени компиляции: длину массива. Вы не можете просто позвонить MyFunc(), вы должны позвонить MyFunc<21>(). И так как аргументы шаблона должны быть определяемыми временем компиляции, сам пользователь не может предоставить значение, которое не определено во время компиляции.

Поскольку параметры шаблона являются всегда время компиляции определены, вы можете шаблоны гнезда:

template<int ArrayLen> void OtherFunc { 
    MyFunc<ArrayLen + 3>(); 
} 

Эта новая функция шаблона вызывает старый с массивом 3 больше, чем то, что оно было дано.

+0

Удобное объяснение! – Dawson

0

Он говорит, что в шаблоне класса, Dozen<T>, параметр шаблона T также используется в качестве параметризировать переменной-члена, ArrayInClass<T,N>.

Так что, если вы инстанцирует Dozen<T> как, например:

Dozen<float> my_dozen; 

, то компилятор будет генерировать код следующим образом:

class Dozen<float> { 
    public: 
    ArrayInClass<float,12> contents; 
}; 
+0

ok. скажите мне, что означает автор: «Поскольку параметры шаблона в конечном итоге заменяются значениями времени компиляции, они сами могут использоваться для формирования выражений компиляции. Это использовалось в шаблоне ArrayInClass для определения размера массива элементов. Размер массива должно быть ** так называемым константным выражением **, а параметр шаблона N квалифицируется как таковой ». –

+1

@ Mr.Anubis: Вы не разместили код для 'ArrayInClass', поэтому я не могу быть уверен. Но я подозреваю, что у него есть переменная-член как 'T data [N];'. C++ не имеет массивов переменной длины; значение 'N' должно быть известно во время компиляции. –

+0

спасибо, я вставил оставшийся код. –

0

Срезы вы выделены жирным шрифтом, говорят, что, когда вы определяете класс Foo<T>, что T должен быть известен компилятору во время компиляции; то есть он не может быть неполным.

Для каждого типа T что инстанцирование Foo с, компилятор создает совершенно новый тип (Foo<int>, Foo<MyClass> и т.д.), которая не имеет отношение к какому-либо из других типов вы можете экземпляры Foo с, хотя они могут все имеют такое же поведение, как определено в реализации Foo.

В примере, который вы опубликовали, я принимаю ArrayInClass<T,N> создает массив типа T, содержащий N элементов.

Например,

template< typename T, size_t N > 
class ArrayInClass 
{ 
    T myArr_[N]; 

public: 
    // public interface 
}; 

Массив в пределах класса не динамически выделяются в настоящее время объявлен как статический массив, хотя она использует N как длина массива. Это возможно, потому что компилятор заменит длину, указанную вами как параметр шаблона, при создании экземпляра ArrayInClass. Было бы невозможно, если компилятор не смог определить N во время компиляции.

1

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

#include <iostream> 

template <int N> 
struct Factorial 
{ 
    enum { value = N * Factorial<N-1>::value }; 
}; 

template <> 
struct Factorial<1> 
{ 
    enum { value = 1 }; 
}; 

// example use 
int main() 
{ 
    std::cout << Factorial<5>::value << endl; 
    return 0; 
} 
+0

Может быть хорошо известно, но я никогда не видел его раньше. Очень круто! – Daniel

0

ОК, основанный на ваш комментарий, вот пикантная рассуждения:

Предположим, у вас есть шаблон:

template <typename T> struct Moo { }; 

Теперь мы знаем, что сказать Moo<Bar>, Bar должен быть (типа это), известных во время компиляции. Пока нет проблем.

Теперь предположим, что мы хотим построить некоторый класс:

class MyClass 
{ 
    int   a; 
    double  b; 
    Moo<double> m; 
}; 

Это тоже хорошо, потому что Moo<double> является допустимым типом. Опять же, никаких проблем. Но теперь давайте обобщим MyClass и сделать его шаблон:

template <typename U> 
class MyClass 
{ 
    int a; 
    U  b; 
    Moo<U> m; 
}; 

Теперь снова сказать MyClass<Zoo>, Zoo должен быть известен во время компиляции. Из-за этого зависимый тип Moo<U> заменяется на Moo<Zoo> в MyClass<Zoo>, и поскольку Zoo известен, этот тип элемента теперь также известен.

Жирный текст, который вы цитируете, просто говорит, что это рассуждение работает, и вы получаете что-то действительное.

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

0

Я думаю, вам будет очень сложно найти более четкое выражение того, что говорят цитируемые параграфы. Думаю, они очень хорошо объясняют проблему.

Если вы все еще нужна помощь, чтобы понять идею, попытаться понять три ключевых слов первого: шаблон, параметр и аргумент. Вот мое определение:

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

Шаблон - это функция, которая может быть параметризована. В вашем примере, Дюжина шаблон:

template <typename T> 
class Dozen 
{ 
... 
}; 

где T является параметром Dozen шаблона. Вкратце, T является параметром шаблона .

Возможно, простая аналогия поможет. Подумайте о шаблоне (здесь это Dozen) как литье скульптуры, которое может быть заполнено жидким материалом, который будет установлен внутри литого, приняв его форму и в конечном итоге произведет скульптуру. Теперь параметр T похож на жидкий материал (резина, металл, стекло и т. Д.).), который даст персонажу скульптуры конкретные. Таким образом, вы можете использовать тот же актер, чтобы сделать серию похожих скульптур.

Таким образом, полость в литой форме представляет собой шаблон шаблона, где вы ставите шаблонные аргументы, где вы заполняете шаблон.

Итак, это примерно идея параметризации в метапрограммировании.

Переход на аргумент шаблона и пример с комментариями:

// T states parameter of Dozen template 
template <typename T> 
class Dozen 
{ 
    // the T is argument used to instantiate concrete type from another template 
    ArrayInClass<T,12> contents; 
}; 

Здесь вы можете думать о функции вызова другой функции и передача параметров:

void foo(int a) 
{ 
    bar(a); 
} 

foo не использует себя , но передает его как аргумент bar. Аналогичным образом, дюжина перенаправляет свой собственный параметр шаблона T в качестве аргумента для шаблона ArrayInClass для создания конкретного типа этого типа ArrayInClass.

И наконец, T - выражение времени компиляции. Это означает, что он дает значение во время компиляции. (Выражения - это функции языка программирования, которые дают значение). Величина выражения представляет собой тип (T) или числовую константу().

ArrayInClass<T,12> также является выражением времени компиляции, которое дает экземпляр шаблона ArrayInClass, создающего конкретный тип. В скором времени выражение типа компиляции может быть использовано для построения другого выражения времени компиляции - получения другого (сложного) значения.

В метапрограммировании, это не стоит думать о значение как числа или строки. Тип также является значением здесь.

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