我注意到函数模板中静态变量初始化的一个奇怪的行为.请考虑以下示例:
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*