【问题标题】:Why does my template specialization get compiled if it doesn't get executed?如果我的模板特化没有被执行,为什么它会被编译?
【发布时间】:2014-04-12 19:28:45
【问题描述】:

注意:我知道我在这里做的大部分事情在 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&lt;float&gt;() 的编译器错误真的很难看。所以我尝试为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&lt;float&gt;(),该静态断言的编译也会失败。更奇怪的是,如果我还删除了静态断言并简单地返回 0,那么代码就可以编译并运行,而不会打印“getter 4”!

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

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

【问题讨论】:

  • 如果你写一个函数模板,如果实例化,无论如何都会产生错误,编译器可以直接产生错误。例如,如果您编写一个忽略其第二个参数并在那里传递 RequestedType 的 StaticAssert,编译器会让它通过。
  • @Marc:我不确定我明白了:你能扩展一下吗?
  • 就拿这个在线:template&lt;class&gt;void f(){static_assert(false,"");}。编译器拒绝它,尽管我从未实例化 f.问题是,这个模板不存在任何有效的实例化,这是非法的。
  • 模板 struct StaticAssert {};这是缺少测试方法。

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


【解决方案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;
    }
};

【讨论】:

    猜你喜欢
    • 2014-07-03
    • 1970-01-01
    • 2012-12-25
    • 1970-01-01
    • 2011-06-21
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多