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

sus*_*att 5 c++ templates template-specialization template-meta-programming

注意:我知道我在这里做的很多事情在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];
    }
};
Run Code Online (Sandbox Code Playgroud)

以下是您使用它的方式:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好,花花公子,满足上述所有要求.但是,编译器错误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;
    }
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

D D*_*mmr 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;
    }
};
Run Code Online (Sandbox Code Playgroud)