/************************************************************************/
/* Copyright (c) 2009, Roc King
All rights reserved.
Redistribution and use in source and binary forms,
with or without modification, are permitted
provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and other materials provided with the distribution.
3. Neither the name of the Tju nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/************************************************************************/
/**
这份代码详细介绍了使用SFINAE技术实现is_not_buildin_type的原理。
按顺序由上往下阅读即可。
*/
#include <stdio.h>
#include <iostream>
/** ------------------------------------------------------------------------ */
#define DEFINITION(prefix) \
prefix none {}; \
prefix data { int i; }; \
prefix function { void f(int ) {} }; \
prefix both { int i; double f() { return 0.0; } };
namespace structures { DEFINITION(struct) }
namespace s = structures;
namespace classes { DEFINITION(class) }
namespace c = classes;
namespace unions { DEFINITION(union) }
namespace u = unions;
#undef DEFINITION
/**
上面对class、struct、union分别定义了:
none 有数据成员,无成员函数
data 有数据成员,没成员函数
function 无数据成员,有成员函数
both 有数据成员,有成员函数
*/
/** ------------------------------------------------------------------------ */
void test_pointer_to_data_member() {
// 一旦某个类型不是基本数据类型,就可以定义成员指针(数据成员指针,成员函数指针)。
// 即使它没有数据成员或者成员函数。
// s::none 并没有数据成员或者成员函数。
int s::none::* p; //但是可以定义一个指向s的数据成员指针,只要类型不是void
// void s::none::* p2; //error C2182: 'p2' : illegal use of type 'void'
// 同时,在C++中,字面值0可以隐式转换到任何指针类型。
p = 0; // ok
double s::none::* p3 = 0; // ok
// 但是,如果某类型没有对应类型的数据成员,就不能用数据成员指针去指向它。
int s::data::* p4 = 0;
p4 = &s::data::i; // ok
double s::data::* p5 = 0;
// p5 = &s::data::i;
// error C2440: '=' : cannot convert from 'int structures::data::* ' to 'double structures::data::* '
(void)p3; (void)p5;
}
// 这个是比较完整的测试
void test_pointer_to_data_member_integrate();
/** ------------------------------------------------------------------------ */
void test_pointer_to_member_function() {
// 同理,一旦某个类型不是基本类型,就可以定义指向该类型的成员函数的指针。
// 并且字面值0可以隐式转换到该指针。
int (u::none::* p1)(void) = 0;
double (u::none::* p2)(double) = 0;
// 如果该类型确实有匹配的成员函数,可以使用该成员函数给指针赋值。
double (u::both::* p3 )(void) = &u::both::f;
void (u::function::* p4)(int) = &u::function::f;
// 否则不能赋值
// double (u::both::* p5 )(void) = &u::function::f;
//error C2440: 'initializing' : cannot convert from 'void (__thiscall unions::function::* )(int)' to 'double (__thiscall unions::both::* )(void)'
// void (u::function::* p6)(int) = &u::both::f;
//error C2440: 'initializing' : cannot convert from 'double (__thiscall unions::both::* )(void)' to 'void (__thiscall unions::function::* )(int)'
(void)p1; (void)p2; (void)p3; (void)p4;
}
// 这个是比较完整的测试
void test_pointer_to_member_function_integrate();
/** ------------------------------------------------------------------------ */
/**
那么,测试一个类型是否是内建类型的“一个”方法就是
*/
namespace SFINAE {
class true_type { char dummy; true_type(); };
class false_type { char dummy[2]; false_type(); };
// sizeof(true_type)!=sizeof(false_type)
template<class C>
true_type is_not_buildin_type_test(int C::* pointer_to_data_member);
template<typename T>
false_type is_not_buildin_type_test();
void test_theory() {
using namespace std;
/* 在当前名字空间下, is_not_buildin_type_test是2个函数模板的名字:
template<class C>
true_type is_not_buildin_type_test(int C::* pointer_to_data_member);
(以下简称模板1)
template<typename T>
false_type is_not_buildin_type_test();
(以下简称模板2)
它们相互构成重载。
*/
cout<<sizeof( is_not_buildin_type_test<c::none>(0) )<<endl;
// 0 可以隐式转化成 int c::none::* ,可以匹配模板1 (with C = c::none)
// 这里int是无关紧要的, 只要不是void就行
// 当然使用其他类型的数据成员指针 T C::* (T!=void)
// 或者成员函数指针进行测试 T (C::*)(paramter_list),也是可以的
// 只是int C::* 写起来比较方便。
// 因为() 可以匹配任何类型的对象,
// 所以 0 也可以匹配模板2
// 又因为()处于重载选择优先级中的最底层,所以最终匹配模板1。
// 注意,此处模板2不能使用(int),或者(T*)因为它的优先级高于(int C::*)
cout<<sizeof( is_not_buildin_type_test<double>(0) )<<endl;
// 0 不能隐式转换成 int double::*,也就不能匹配模板1 ( with C=double )
// 但是还有一个“补救”的函数模板2,可以匹配任何类型的对象。
// 又因为SFINAE(Substitution failure is not an error)机制
// 所以对模板1的失败的匹配并不报错。
}
// 还有一些细节
// 比如is_not_buildin_type_test并没有实现。
// 但是因为它同时也没有被调用, 而仅仅是用sizeof测试它的返回值,所以不算错误。
// 也防止了客户无意调用这个函数。
// 如何得知哪个is_not_buildin_type_test被重载选中?
// 是通过返回值的大小不同来区分的。
// 所以就需要true_type和false_type这2个东西。
// 其实更合理的命名应该是small_type和big_type。
// 同时,它们声明有私有的构造函数,并且不实现,防止客户使用这2个类。
// 还因为is_not_buildin_type_test并没有真正实现
// 也就没有真正返回true_type或者false_type
// 所以没有实现true_type和false_type的构造函数也没关系。
// 一切都因为sizeof ……
/** -------------------------------------------------------------------- */
/**
将这种方法再包装一下
(避免客户去使用sizeof( xxx ) == sizeof( ture_type )等等)
就得到
*/
template<typename T>
class is_not_buildin_type {
is_not_buildin_type();
public:
enum { value =
sizeof(true_type)==sizeof( is_not_buildin_type_test<T>(0) ) };
};
// 或者将true_type,false_type,定义为它的内嵌类型。
// 同时将is_not_buildin_type_test定义为它的静态成员函数。
template<typename T>
class is_not_buildin_type2 {
is_not_buildin_type2();
// 因为是内嵌的private,客户不能访问
// 所以可以随意一点
typedef char small_t;
struct big_t { small_t dummy[2]; };
template<typename U>
static big_t test(void (U::*)(short, float) );
// 只要是成员指针就ok,无论是数据成员指针还是成员函数指针。
// 也无论类型,签名如何。
template<typename U>
static small_t test();
// 注意补救函数现在返回small_t
public:
// 但这也是无关紧要的,因为small_t和big_t只是告之哪个重载被选中的方式。
// 只要这里处理好对应就可以了。
enum { value= sizeof(big_t)==sizeof( test<T>(0) ) };
};
void test_wrapper() {
using namespace std;
cout<<is_not_buildin_type<c::data>::value<<endl;
cout<<is_not_buildin_type<u::both>::value<<endl;
cout<<is_not_buildin_type<float>::value<<endl;
cout<<is_not_buildin_type2<c::data>::value<<endl;
cout<<is_not_buildin_type2<u::both>::value<<endl;
cout<<is_not_buildin_type2<float>::value<<endl;
}
// 一个更完整的测试
void test_wrapper_integrate();
}
/** ------------------------------------------------------------------------ */
/**测试一个类型是否是内建类型的另一个方法,需要更少的技巧。*/
namespace partial_specialization {
template<typename T>
struct is_not_buildin_type { enum { value=true }; };
// T不是一个内建类型
// 除非
template<>
struct is_not_buildin_type<int> { enum { value=false}; };
// T是int
template<>
struct is_not_buildin_type<unsigned int> { enum { value=false}; };
// T是unsigned int
// .. more ..
}
int main()
{
using namespace std;
test_pointer_to_data_member();
test_pointer_to_data_member_integrate();
test_pointer_to_member_function();
test_pointer_to_member_function_integrate();
cout<<endl;
SFINAE::test_theory();
cout<<endl;
SFINAE::test_wrapper();
cout<<endl;
SFINAE::test_wrapper_integrate();
}
void test_pointer_to_data_member_integrate() {
// to do
}
void test_pointer_to_member_function_integrate() {
// to do
}
namespace SFINAE {
void test_wrapper_integrate() {
// to do
}
}
这个代码已经能很完美的解释了~
今天看了C++ Templates 的15章.. 收获颇多.. 以前的很多疑惑都比较开朗了~
posted on 2009-03-16 23:32
Charlie 侯杰 阅读(2651)
评论(3) 编辑 收藏 引用