我注意到函数模板中静态变量初始化的一个奇怪的行为.请考虑以下示例:
- 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");
- }
只要测试中的f是静态的,我希望createFile只被调用一次.但是,它被调用两次.
花了一些时间来解决问题,我注意到从测试中的参数中删除const引用修复了它.另一个有趣的事情是传递给函数的字符串的长度也会影响初始化:当参数的长度相等时,静态变量只初始化一次,否则会发生新的初始化.
解决方法
文字“一”是一个const char [4].
这段代码:
- test("one")
理想情况下会调用test(const char(&)[4])
这适用于测试(const T&)(因为const char(&)[4]可以绑定到const char(const&)[4]).
但它无法用于测试(T t),因为您无法按值传递字符串文字.它们通过引用传递.
但是,const char [4]可以衰减为const char *,它可以匹配模板< class T> void func(T t).
证据在于布丁:
- #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");
- }
示例结果(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
这是一个带有demangled名称的版本(将在clang和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,&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");
- }
预期产量:
- 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*