模板函数

时间:2017-01-11 16:10:46

标签: c++ c++11 templates static

我注意到函数模板中静态变量初始化的一个奇怪的行为。请考虑以下示例:

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中的test是静态的,我预计createFile只会被调用一次。但是,它被调用两次。

花了一些时间来解决这个问题,我注意到从test中的参数中删除const引用会修复它。另一个有趣的事情是传递给函数的字符串长度也会影响初始化:当参数长度相等时,静态变量只初始化一次,否则会发生新的初始化。

有人可以解释一下吗?除了上面提到的解决方案/解决方案之外,我们非常欢迎。

2 个答案:

答案 0 :(得分:6)

字面意思“一”是const char [4]

此代码:

test("one")
理想情况下,

会致电test(const char (&)[4])

这适用于test(const T&)(因为const char (&) [4]可以绑定到const char (const&) [4])。

但它不适用于test(T t),因为您无法按值传递字符串文字。它们通过引用传递。

但是,const char[4]可以衰减到const char*,可以匹配template<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, 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*

答案 1 :(得分:0)

作为@ RichardHodges的答案的补充,解释了为什么使用不同的实例,很容易只强制一个,因为数组可以通过显式模板实例衰减到指针:

test<const char *>("one");
test<const char *>("two");
test<const char *>("three");

导致createFile的一次调用。

事实上(如BoBTFish评论中所述),这正是你写的时候发生的事情:

template <typename T>
void test(const T t)

无论数组的大小如何,数组都会自动衰减到const char *,因为C ++不允许直接分配数组。

BTW,void main() 。始终使用int main()并明确返回。