如果我的模板专用化没有被执行,为什么会被编译?

Why does my template specialization get compiled if it doesn't get executed?

本文关键字:执行 为什么 编译 我的 专用 如果      更新时间:2023-10-16

注意:我知道我在这里做的很多事情在C++11中会更容易,但我不能在我的项目中使用它。

我正在制作一个内容管理系统。基本要求是:

  1. 必须能够定义"内容持有者"类,这些类持有任意数量的向量,每个向量持有不同类型的值。例如,IntHolder可以保持vector<int>FloatAndBoolHolder可以保持vector<float>vector<bool>,依此类推
  2. 内容持有者类必须具有get<>()方法。get<>()的模板参数是一种类型。如果内容持有者具有该类型的向量,则get<>()必须从该向量返回一个值,否则对get<>()的调用必须生成编译器错误。例如,如果我有一个IntHolder对象,那么对其调用get<int>()将从其向量返回int,但对其调用get<float>()将生成编译器错误

我设法想出了一个解决这一切的办法。警告,前方模板递归:

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int value = 'A';

// helper struct that saves us from partially specialized method overloads
template < class RequestedType, class ActualType, class TContentHolder >
struct Getter;

// holds a vector of type TContent, recursively inherits from holders of other types
template < class TContent, class TAddContentHolders >
class ContentHolder : public ContentHolder< typename TAddContentHolders::ContentType, typename TAddContentHolders::AdditionalContentTypes >
{
public:
    typedef TContent ContentType;
    typedef TAddContentHolders AdditionalContentTypes;
private:
    typedef ContentHolder< typename TAddContentHolders::ContentType, typename TAddContentHolders::AdditionalContentTypes > ParentType;
public:
    vector< ContentType > mVector;
    ContentHolder()
    {
        for ( int i = 0; i < 5; ++i )
        {
            mVector.push_back( ContentType(value++) );
        }
    }
    virtual ~ContentHolder() {}
    template < class RequestedType >
    RequestedType get()
    {
        return Getter< RequestedType, ContentType, ContentHolder < TContent, TAddContentHolders > >::get(this);
    }
};
// specialization for ending the recursion
template < class TContent >
class ContentHolder< TContent, bool >
{
public:
    typedef TContent ContentType;
    typedef bool AdditionalContentTypes;
    vector< ContentType > mVector;
    ContentHolder()
    {
        for ( int i = 0; i < 5; ++i )
        {
            mVector.push_back( ContentType(value++) );
        }
    }
    virtual ~ContentHolder() {}
    template < class RequestedType >
    RequestedType get()
    {
        return Getter< RequestedType, ContentType, ContentHolder< ContentType, bool > >::get(this);
    }
};

// default getter: forwards call to parent type
template < class RequestedType, class ActualType, class TContentHolder >
struct Getter
{
    static RequestedType get(TContentHolder* holder)
    {
        cout << "getter 1" << endl;
        return Getter< RequestedType, typename TContentHolder::ContentType, typename TContentHolder::AdditionalContentTypes >::get(holder);
    }
};
// specialized getter for when RequestedType matches ActualType: return value from holder
template < class RequestedType, class TContentHolder >
struct Getter< RequestedType, RequestedType, TContentHolder >
{
    static RequestedType get(TContentHolder* holder)
    {
        cout << "getter 2" << endl;
        return holder->mVector[0];
    }
};
// specialized getter for end of recursion
template < class RequestedType >
struct Getter< RequestedType, RequestedType, bool >
{
    static RequestedType get(ContentHolder< RequestedType, bool >* holder)
    {
        cout << "getter 3" << endl;
        return holder->mVector[0];
    }
};

以下是使用方法:

// excuse the ugly syntax
class MyHolder : public ContentHolder< int, ContentHolder< bool, ContentHolder< char, bool > > >
{
};
int main() {
    MyHolder h;
    cout << h.get<int>() << endl; // prints an int
    cout << h.get<bool>() << endl; // prints a bool
    cout << h.get<char>() << endl; // prints a char
    //cout << h.get<float>() << endl; // compiler error
    return 0;
}

这一切都很好,满足了上述所有要求。然而,get<float>()的编译器错误确实很难看。因此,我尝试为Getter引入另一种专门化,它解释了当我们到达类层次结构的末尾时仍然没有找到匹配类型的情况:

// static assert helper
template <bool b>
struct StaticAssert {};
template <>
struct StaticAssert<true>
{
    static void test(const string& s) {}
};

template < class RequestedType, class NonMatchingType >
struct Getter< RequestedType, NonMatchingType, bool >
{
    static RequestedType get(ContentHolder< NonMatchingType, bool >* holder)
    {
        cout << "getter 4" << endl;
        StaticAssert<false>::test("Type not in list");
        return 0;
    }
};

但这样,即使我不调用get<float>(),编译也会在该静态断言上失败。更奇怪的是,如果我也删除静态断言并简单地返回0,那么代码编译和运行时就不会打印"getter 4"!

问题:给出了什么?据我所知,模板只有在需要的时候才会被实例化,但Getter 4从来没有被执行过。编译器为什么实例化Getter 4?

示例:http://ideone.com/TCSi6G

编译器可以编译"getter 4"成员函数,因为代码不依赖于模板参数。如果您使代码依赖于模板参数,则编译器无法编译它,除非您使用特定类型实例化它。实现这一点的一个简单方法是在静态断言中使用类型。

template < class RequestedType, class NonMatchingType >
struct Getter< RequestedType, NonMatchingType, bool >
{
    static RequestedType get(ContentHolder< NonMatchingType, bool >* holder)
    {
        cout << "getter 4" << endl;
        StaticAssert<sizeof(NonMatchingType) == 0>::test("Type not in list");
        return 0;
    }
};