Static variable inside template function

前端 未结 7 1912
一向
一向 2020-12-14 07:10

In C++, if you define this function in header.hpp

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


        
相关标签:
7条回答
  • 2020-12-14 07:14
    • templates will only actually be turned into code once they're instantiated (i.e. used)
    • headers are not to be used for implementation code, but only for declarations
    0 讨论(0)
  • 2020-12-14 07:15

    Yes, it is "normal", but whatever you try to achieve with this "feature" maybe questionable. Try explain why you want to use local static variable, may be we can come up with a cleaner way to do it.

    The reason this is normal is because of the way template functions are compiled and linked. Each translation unit (the two .cpp in your case) gets see their own copy of incAndShow and when the program is linked together, the two incAndShow will be merged into one. If you declare your regular function inline in the header file, you'll get similar effect.

    0 讨论(0)
  • Just so i understand your question. You are asking if it is normal for each version of the templated function to have its own instance of myStaticVar. (for example: incAndShow<int> vs. intAndShow<float> The answer is yes.

    Your other question is, if two files include the header containing the template function, will they still share the static variable for a given T. I would say yes.

    0 讨论(0)
  • 2020-12-14 07:22

    Take this example that shows the behaviour is absolutely expected:

    #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;
    }
    

    You get: 5 10 10 10 10

    The above shows that the change in static variable is only for type "int" and hence in your case you don't see any problem.

    0 讨论(0)
  • 2020-12-14 07:24

    You can rely on this. The ODR (One Definition Rule) says at 3.2/5 in the Standard, where D stands for the non-static function template (cursive font by me)

    If D is a template, and is defined in more than one translation unit, then the last four requirements from the list above shall apply to names from the template’s enclosing scope used in the template definition (14.6.3), and also to dependent names at the point of instantiation (14.6.2). If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.

    Of the last four requirements, the two most important are roughly

    • each definition of D shall consist of the same sequence of tokens
    • names in each definition shall refer to the same things ("entities")

    Edit

    I figure that this alone is not sufficient to guarantee that your static variables in the different instantiations are all the same. The above only guarantees that the multiple definitions of the template is valid. It doesn't say something about the specializations generated from it.

    This is where linkage kicks in. If the name of a function template specialization (which is a function) has external linkage (3.5/4), then a name that refers to such a specialization refers to the same function. For a template that was declared static, functions instantiated from it have internal linkage, because of

    Entities generated from a template with internal linkage are distinct from all entities generated in other translation units. -- 14/4

    A name having namespace scope (3.3.6) has internal linkage if it is the name of [...] an object, reference, function or function template that is explicitly declared static -- 3.5/3

    If the function template wasn't declared with static, then it has extern linkage (that, by the way, is also the reason that we have to follow the ODR at all. Otherwise, D would not be multiply defined at all!). This can be derived from 14/4 (together with 3.5/3)

    A non-member function template can have internal linkage; any other template name shall have external linkage. -- 14/4.

    Finally, we come to the conclusion that a function template specialization generated from a function template with external linkage has itself external linkage by 3.5/4:

    A name having namespace scope has external linkage if it is the name of [...] a function, unless it has internal linkage -- 3.5/4

    And when it has internal linkage was explained by 3.5/3 for functions provided by explicit specializations, and 14/4 for generated specializations (template instantiations). Since your template name has external linkage, all your specializations have external linkage: If you use their name (incAndShow<T>) from different translation units, they will refer to the same functions, which means your static objects will be the same in each occasion.

    0 讨论(0)
  • 2020-12-14 07:34

    The difference when you create the function template is that it has external linkage. The same incAndShow will be accessible from all translation units.

    Paraphrasing from C++ standard working draft N2798 (2008-10-04): 14 part 4: a non member function template can have internal linkage, others always have external linkage. 14.8 point 2: every specialization will have its own copy of the static variable.

    Your function template should have external linkage unless you declare it in the unnamed namespace or something. So, for each T that you use with your function template, you should get one static variable used throughput the program. In other words, it's OK to rely on having only one static variable in the program for each instantiation of the template (one for T==int, one for T==short, etc).

    As an aside, this can lead to weird situations if you define incAndShow differently in different translation units. E.g., if you define it to increment in one file and the decrement in another file (without specifying internal linkage by putting the function into the unnamed namespace) both will end up sharing the same function, which will effectively be chosen at random at compile time (with g++ it depends on the order the object files are given on the command line).

    0 讨论(0)
提交回复
热议问题