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

时间:2014-04-12 19:28:46

标签: c++ templates template-specialization template-meta-programming

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

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

  1. 必须能够定义内容持有者&#34;包含任意数量向量的类,每个向量包含不同类型的值。例如。 IntHolder可以包含vector<int>FloatAndBoolHolder可以包含vector<float>vector<bool>,依此类推。
  2. 内容持有者类必须采用get<>()方法。 get<>()的模板参数是一种类型。如果内容持有者具有该类型的向量,则get<>()必须从该向量返回一个值,否则对get<>()的调用必须生成编译器错误。例如。如果我有一个IntHolder对象,那么在其上调用get<int>()将从其向量返回int,但在其上调用get<float>()会产生编译错误。
  3. 我设法提出了解决所有这些问题的解决方案。警告,模板递归:

    #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,代码编译并运行而不会打印&#34; getter 4&#34;!

    问题:是什么给出的?根据我的理解,模板只有在需要时才会被实例化,但Getter 4永远不会被执行。为什么编译器实例化Getter 4?

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

1 个答案:

答案 0 :(得分:1)

编译器可以编译“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;
    }
};