如果我的模板专用化没有被执行,为什么会被编译?
Why does my template specialization get compiled if it doesn't get executed?
注意:我知道我在这里做的很多事情在C++11中会更容易,但我不能在我的项目中使用它。
我正在制作一个内容管理系统。基本要求是:
- 必须能够定义"内容持有者"类,这些类持有任意数量的向量,每个向量持有不同类型的值。例如,
IntHolder
可以保持vector<int>
,FloatAndBoolHolder
可以保持vector<float>
和vector<bool>
,依此类推 - 内容持有者类必须具有
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;
}
};
相关文章:
- 为什么catch中的代码没有被执行
- 为什么g++在未执行的代码处标记强制转换错误
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 为什么我的最后一个 ELSE 条件无法正确执行
- 为什么即使调用了析构函数,C++11 中的分离线程也可以执行
- 为什么切换 for 循环的顺序会显著改变执行时间?
- 为什么我的信号处理程序只执行一次?
- 为什么每次执行时函数的地址都不同?
- 执行此代码时,它不显示任何输出.为什么?
- 为什么循环体中的一个基本算术运算执行得比两个算术运算慢
- 为什么C++可执行文件在与较新的libstdc++.so链接时运行得更快?
- 为什么 printf 在 C++ 中的执行速度比 cout 快?另外scanf比cin慢,为什么?
- 为什么要执行两次位移((x >> 4)<< 4)?
- 为什么其中一个 while 循环没有被执行 (C++)?
- 为什么 std::chrono 在测量循环和编译器优化的并行 OpenMP 的执行时间时不起作用?
- C++ |为什么执行此程序后我会得到奇怪的字符?
- 如果函数包含静态变量,为什么编译器不执行内联?
- 为什么程序运行时我的第二个循环不执行?
- 为什么变量的打印地址在每次执行时都会打印随机值,即使它是 C 中的逻辑地址?
- 使用一个参数执行?为什么