2009-06-15 3 views
24

В C++, если определить эту функцию в header.hppстатическая переменная внутри шаблона функции

void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

и включить header.hpp по крайней мере двух файлов .cpp. Тогда у вас будет multiple definition of incAndShow(). Что ожидается. Однако, если добавить шаблон функции

template <class T> 
void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

тогда у вас не будет каких-либо multiple definition of ошибку. Аналогично, два разных .cpp, вызывающих функцию с тем же шаблоном (например, incAndShow<int>()), будут делиться myStaticVar. Это нормально? Я задаю этот вопрос, потому что я полагаюсь на эту «функцию» (разделяя статическую переменную), и я хочу быть уверенным, что это не только моя реализация.

+0

+1 Хороший вопрос. Теперь я реализую что-то, что «полагается на эту функцию» *. – Nawaz

ответ

27

Вы можете положиться на это. ODR (One Definition Rule) говорит, что в 3.2/5 в Стандарте, где D обозначает шаблон нестатической функции (скоропись шрифтов мной)

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

Из последних четырех требований, два наиболее важные примерно

  • каждого определения D состоит из одной и той же последовательности лексем
  • имен в каждом определении, должны относиться к тем же вещам ("сущности")

Редактировать

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

Это где связи умирает. Если имя шаблон функции специализации (которая является функцией) имеет внешнее связывание (3.5/4), то имя, которое относится к такой специализации относится к одной и той же функции. Для шаблона, который был объявлен статическим, функции, созданные из него, имеют внутреннюю связь, из-за

Объекты, созданные из шаблона с внутренней связью, отличны от всех объектов, сгенерированных в других единицах перевода. -- 14/4

Пространство имен Объем имя, имеющий (3.3.6) имеет внутреннюю связь, если это имя [...] объект, ссылки, функция или шаблон функции, которая явным образом объявлена ​​статическая -- 3.5/3

Если шаблон функции не был объявлен со статикой, то он имеет внешнюю связь (что, кстати, также является причиной того, что мы должны следовать за ODR вообще, иначе D не будет размножаться вообще!). Это может быть получено из 14/4 (вместе с 3.5/3)

Шаблон функции не являющегося членом может иметь внутреннюю связь; любое другое имя шаблона должно иметь внешнюю связь. -- 14/4.

Наконец, мы пришли к выводу, что шаблон функции специализация генерируется из шаблона функции с внешним связыванием имеет себя внешнее связывание с 3.5/4:

Имени, имеющее область пространства имен имеет внешнее связывание, если оно имя [...] функция, если она не имеет внутренней связи -- 3.5/4

И когда она внутренняя связь объясняется 3.5/3 для функций обеспечивается явным специализации и 14/4 для созданных специализаций (экземпляров шаблонов). Поскольку ваше имя шаблона имеет внешнюю связь, все ваши специализации имеют внешнюю связь: если вы используете свое имя (incAndShow<T>) из разных единиц перевода, они будут ссылаться на те же функции, что означает, что ваши статические объекты будут одинаковыми в каждом случае.

5

Просто так я понимаю ваш вопрос. Вы спрашиваете, нормально ли для каждой версии шаблонизированной функции иметь свой собственный экземпляр myStaticVar. (например: incAndShow<int> vs. intAndShow<float> Ответ:

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

0

Шаблоны создаются при необходимости, а это значит, что компилятор (в этом случае и компоновщик?) Будет удостовериться, что вы не получите несколько экземпляров одного и того же шаблона, а также только те экземпляры шаблонов, которые вы необходимо - в вашем случае только экземпляр incAndShow<int>() и ничего другого (иначе компилятор должен был бы попытаться создать экземпляр для каждого типа, который не имеет смысла).

Таким образом, я предполагаю, что те же методы, которые он использует, чтобы выяснить, для какого типа для создания шаблона шаблона не удается дважды создать экземпляр для одного и того же типа, например. только один экземпляр incAndShow<int>()

Это отличается от кода без шаблонов.

-3
  • шаблонов фактически только быть включены в код, как только они инстанцированы (т.е. используется)
  • заголовков не должны быть использованы для реализации коды, но только для объявлений
+0

ребята, если вы беспокоитесь о снижении, я бы хотел оставить комментарий, что касается проблемы - очевидно, исходный вопрос был довольно неопределенным ... – none

+2

"заголовки не должны использоваться для реализации кода, но только для объявлений " Не верно для шаблонов. Вам нужно определить («реализовать») шаблоны в заголовке. экспорт был способом, который оказался слишком сложным и вообще не реализован. – Suma

0

Да, «нормальный», но все, что вы пытаетесь достичь с помощью этой «функции», может оказаться спорным. Попробуйте объяснить, почему вы хотите использовать локальную статическую переменную, может быть, мы можем придумать более чистый способ сделать это.

Причина, по которой это нормально, связано с тем, что функции шаблона скомпилированы и связаны. Каждая единица перевода (два .cpp в вашем случае) получает свою собственную копию incAndShow, а когда программа связана вместе, два incAndShow будут объединены в один. Если вы объявите свою регулярную функцию inline в файле заголовка, вы получите аналогичный эффект.

1

Разница при создании шаблона функции заключается в том, что она имеет внешнюю связь. То же самое incAndShow будет доступно из всех единиц перевода.

Перефразируя из стандартной рабочей схемы C++ N2798 (2008-10-04): 14 часть 4: шаблон функции без членства может иметь внутреннюю связь, другие всегда имеют внешнюю связь. 14.8 пункт 2: каждая специализация будет иметь свою собственную копию статической переменной.

Ваш шаблон функции должен иметь внешнюю связь, если вы не объявите ее в неназванном пространстве имен или чем-то еще. Таким образом, для каждого T, который вы используете с вашим шаблоном функции, вы должны получить одну статическую переменную, используемую для пропускной способности программы. Другими словами, вполне нормально полагаться только на одну статическую переменную в программе для каждого экземпляра шаблона (один для T == int, один для T == short и т. Д.).

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

-1

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

#include <iostream> 

template <class T> class Some 
{ 
public: 
    static int stat; 
}; 

template<class T> 
int Some<T>::stat = 10; 

void main() 
{ 
    Some<int>::stat = 5; 
    std::cout << Some<int>::stat << std::endl; 
    std::cout << Some<char>::stat << std::endl; 
    std::cout << Some<float>::stat << std::endl; 
    std::cout << Some<long>::stat << std::endl; 
} 

Вы получаете: 5 10 10 10 10

выше показывает, что изменение статической переменной только для типа «Int» и, следовательно, в вашем если вы не видите никаких проблем.

+3

-1: этот пример демонстрирует статический член класса; вопрос задает статическую переменную внутри функции. –

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