Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ static variable initialization inside a template function

I noticed quite a strange behavior of static variable initialization in function templates. Consider the following example:

MyFile * createFile()
{
    std::cout << "createFile" << std::endl;
    return nullptr;
}

template <typename T>
void test(const T& t)
//void test(T t)
{
    static MyFile *f = createFile();
}

void main()
{
    test("one");
    //test("two");
    test("three");
}

As long as f in test is static, I expected createFile to be called only once. However, it is called twice.

Having spent some time playing around with the problem, I noticed that removing const reference from the argument in test fixes it. Another interesting thing is that the length of the string passed to the function also affects the initialization: when the length of parameters is equal, static variable is initialized only once, otherwise, a new initialization takes place.

Could somebody explain this? Solutions/workarounds apart from the mentioned ones are very welcome.

like image 319
mentalmushroom Avatar asked Jan 11 '17 16:01

mentalmushroom


People also ask

What is the initialization of static variables in C?

Initialization of static variables in C. When static keyword is used, variable or data members or functions can not be modified again. It is allocated for the lifetime of program. Static functions can be called directly by using class name. Static variables are initialized only once. Compiler persist the variable till the end of the program.

What are static templates and static variables in C++?

Templates and Static variables in C++. Function templates and static variables: Each instantiation of function template has its own copy of local static variables. For example, in the following program there are two instances: void fun(int ) and void fun(double ). So two copies of static variable i exist.

How many static variables are there in an instantiation of class?

Class templates and static variables: Each instantiation of class template has its own copy of member static variables. For example, in the following program there are two instances Test and Test. So two copies of static variable count exist.

What is a static int variable in C?

1 A static int variable remains in memory while the program is running. ... 2 Static variables are allocated memory in data segment, not stack segment. See memory layout of C programs for details. 3 Static variables (like global variables) are initialized as 0 if not initialized explicitly. ... More items...


1 Answers

The literal "one" is a const char [4].

this code:

test("one")

would ideally like to call test(const char (&)[4])

This works for test(const T&) (because const char (&) [4] can bind to const char (const&) [4]).

But it cannot work for test(T t) because you can't pass string literals by value. They are passed by reference.

However, const char[4] can decay to const char*, which can match template<class T> void func(T t).

The proof is in the pudding:

#include <cstdint>
#include <iostream>
#include <typeinfo>

template <typename T, std::size_t N>
void test_const(const T(&t)[N])
{
    std::cout << __func__ << " for literal " << t << " T is a " << typeid(T).name() << " and N is " << N << std::endl;
}

template <typename T>
void test_mutable(T &t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << typeid(T).name() << std::endl;
}

template <typename T>
void test_const_ref(const T &t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << typeid(T).name() << std::endl;
}

template <typename T>
void test_copy(T t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << typeid(T).name() << std::endl;
}

int main()
{
    test_const("one");
    test_const("three");
    test_mutable("one");
    test_mutable("three");
    test_const_ref("one");
    test_const_ref("three");
    test_copy("one");
    test_copy("three");
}

example results (clang):

test_const for literal one T is a c and N is 4
test_const for literal three T is a c and N is 6
test_mutable for literal one T is a A4_c
test_mutable for literal three T is a A6_c
test_const_ref for literal one T is a A4_c
test_const_ref for literal three T is a A6_c
test_copy for literal one T is a PKc
test_copy for literal three T is a PKc

Here is a version with demangled names (will compile on clang and gcc):

#include <cstdint>
#include <iostream>
#include <typeinfo>
#include <cstdlib>
#include <cxxabi.h>

std::string demangle(const char* name)
{
    int status = -1;
    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    return (status==0) ? res.get() : name ;
}

template <typename T, std::size_t N>
void test_const(const T(&t)[N])
{
    std::cout << __func__ << " for literal " << t << " T is a " << demangle(typeid(T).name()) << " and N is " << N << std::endl;
}

template <typename T>
void test_mutable(T &t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << demangle(typeid(T).name()) << std::endl;
}

template <typename T>
void test_const_ref(const T &t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << demangle(typeid(T).name()) << std::endl;
}

template <typename T>
void test_copy(T t)
{
    std::cout << __func__ << " for literal " << t << " T is a " << demangle(typeid(T).name()) << std::endl;
}

int main()
{
    test_const("one");
    test_const("three");
    test_mutable("one");
    test_mutable("three");
    test_const_ref("one");
    test_const_ref("three");
    test_copy("one");
    test_copy("three");
}

expected output:

test_const for literal one T is a char and N is 4
test_const for literal three T is a char and N is 6
test_mutable for literal one T is a char [4]
test_mutable for literal three T is a char [6]
test_const_ref for literal one T is a char [4]
test_const_ref for literal three T is a char [6]
test_copy for literal one T is a char const*
test_copy for literal three T is a char const*
like image 176
Richard Hodges Avatar answered Nov 03 '22 10:11

Richard Hodges