C++模板问题

bobo_包子 2014-01-05 11:40:13
最近看了下C++ templates,其中有个判断类型的tool,
Original如下:
template<typename T>
class IsFunction
{
private:
typedef struct {} One;
typedef struct { One a[2]; } Two;
template<typename U> static One testFunction(...);
template<typename U> static Two testFunction(U (*)[1] );
public:
enum { YES = sizeof(IsFunction<T>::testFunction<T>(0)) == sizeof(One) };
enum { NO = !YES };
};

测试程序如下:
void fun() {}

template<typename T>
void checkFun(T *t)
{
if(IsFunction<T>::YES)
cout << "is function!" << endl;
else
cout << "is not function!" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
Fun myFun = &fun;
int *pa;
checkFun(pa); // {1}
checkFun(myFun); // {2}
//IsFunction<cl>::YES;
getchar();
return 0;
}

OK这段代码的意思我也明白,主要用了数组和函数重载的一些知识。这段代码是不能编译的,必须做修改。
Modified 1:
template<typename T>
class IsFunction
{
private:
typedef struct {} One;
typedef struct { One a[2]; } Two;
static One testFunction(...);
static Two testFunction(U (*)[1] );
public:
enum { YES = sizeof(IsFunction<T>::testFunction(0)) == sizeof(One) };
enum { NO = !YES };
};


测试结果:不能编译
error:'abstract declarator' array element type cannot be function

Modified 2:
template<typename T>
class IsFunction
{
private:
typedef struct {} One;
typedef struct { One a[2]; } Two;
template<typename U> static One testFunction(...);
template<typename U> static Two testFunction(U (*)[1] );
public:
enum { YES = sizeof(testFunction<T>(0)) == sizeof(One) };
enum { NO = !YES };
};

测试结果:OK

Modified 3:
	typedef struct {} One;
typedef struct { One a[2]; } Two;
template<typename U> static One testFunction(...);
template<typename U> static Two testFunction(U (*)[1] );

放到class外边,测试结果与2相同。

谁能帮解释下,我觉得1不能通过编译的原因是没有使用重载解析
...全文
475 24 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
bobo_包子 2014-01-07
  • 打赏
  • 举报
回复
谢谢各位,晚上回来再好好梳理下
unituniverse2 2014-01-06
  • 打赏
  • 举报
回复
引用 21 楼 supermegaboy 的回复:
[quote=引用 18 楼 unituniverse2 的回复:] 至于非标量的识别,也是利用SFINAE。这里的代码我就直接照搬以前写过的。简单的看,就是测试类型是否能包含成员函数(仅是看能不能“添加”,而这个函数不一定非得存在):

	// If the input template argument is a type that allows to be supplemented a function to, like did to a structure or a class or a
	//  union, the result will be defined as non-zero.
	template < typename _T_ >
	struct Is_Single_Non_Scalar_Object
	{
	private:
		template < typename _t_ >
		static char (& GetRes(void (_t_::*)(void)))[2];

		template < typename _t_ >
		static char GetRes(...);

	public:
		enum nRes { result = static_cast<int>(sizeof(GetRes<_T_>(nullptr))) - 1 };

		Is_Single_Non_Scalar_Object(void) = delete;
		Is_Single_Non_Scalar_Object(const Is_Single_Non_Scalar_Object &) = delete;
	};
数组的识别用普通的型式特化就可以识别出来(估计你很快就想得到,所以代码就不贴了) 然后内嵌一个结构,并特化出一个版本筛选掉数组和非标量等,仅仅留下标量类型进行分析,也就是把楼主的代码都移入结构中的非特化版本部分里去。
留意到那个IsFunction的不足这很好,但描述还差一点点。数组是可以识别为假的,而且也并不是所有非标量类型都会识别为真,完整的漏洞描述应该为:所有不能作为数组元素的非函数类型均会被识别为函数类型,这包括:所有类型的引用、成员函数、抽象类(因为不是最终派生类类型)。[/quote]
孩皮妞野 2014-01-06
  • 打赏
  • 举报
回复
很有含金量的问题。也感谢supermegaboy的精彩回复。膜拜一下。
Adol1111 2014-01-05
  • 打赏
  • 举报
回复
引用 1 楼 Automation_dmu 的回复:
1中 U类型 哪里来?
+1
AndyStevens 2014-01-05
  • 打赏
  • 举报
回复
1中 U类型 哪里来?
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 18 楼 unituniverse2 的回复:
至于非标量的识别,也是利用SFINAE。这里的代码我就直接照搬以前写过的。简单的看,就是测试类型是否能包含成员函数(仅是看能不能“添加”,而这个函数不一定非得存在):

	// If the input template argument is a type that allows to be supplemented a function to, like did to a structure or a class or a
	//  union, the result will be defined as non-zero.
	template < typename _T_ >
	struct Is_Single_Non_Scalar_Object
	{
	private:
		template < typename _t_ >
		static char (& GetRes(void (_t_::*)(void)))[2];

		template < typename _t_ >
		static char GetRes(...);

	public:
		enum nRes { result = static_cast<int>(sizeof(GetRes<_T_>(nullptr))) - 1 };

		Is_Single_Non_Scalar_Object(void) = delete;
		Is_Single_Non_Scalar_Object(const Is_Single_Non_Scalar_Object &) = delete;
	};
数组的识别用普通的型式特化就可以识别出来(估计你很快就想得到,所以代码就不贴了) 然后内嵌一个结构,并特化出一个版本筛选掉数组和非标量等,仅仅留下标量类型进行分析,也就是把楼主的代码都移入结构中的非特化版本部分里去。
留意到那个IsFunction的不足这很好,但描述还差一点点。数组是可以识别为假的,而且也并不是所有非标量类型都会识别为真,完整的漏洞描述应该为:所有不能作为数组元素的非函数类型均会被识别为函数类型,这包括:所有类型的引用、成员函数、抽象类(因为不是最终派生类类型)。
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 15 楼 hooked 的回复:
你确定qualified-id在类的内部也是符合标准的么?那试试gcc编译下面的代码

class A
{
public:
    void A::Foo()
    {
    }
};
那个Foo虽然是qualified-id,但这里的语法结构不合法不是因为Foo是一个qualified-id,而是不符合类的member declaration的语法规定,如果你想看类内使用qualified-id的直接证明,如下条款就是了: 3.4.3.1 Class members [ Note: A class member can be referred to using a qualified-id at any point in its potential scope (3.3.7). —end note ]
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 14 楼 bbs2241 的回复:
这个是笔误 是我自己修改的。 modified 1是不能测试函数的,测试类型的时候是可以的。 测试函数时的error:'abstract declarator' array element type cannot be function 这个错误应该是不能或者没有使用重载版本吧,应该使用static One testFunction(...),但是编译器使用static Two testFunction(U (*)[1] ); 我的理解是编译器在这里不能使用SFINE原则,不知道我的理解对不对。 我就是对这里比较好奇,我也觉得应该是这样。你们可以发表下观点不?
你理解错了,作者用这个IsFunction就是为了说明SFINAE的应用,正因为有了SFINAE,重载解析时才会得出testFunciton(...)这个结果,否则就会发生编译错误了。实际上,使用SFINAE也是C++98/03下无奈的选择,因为C++98/03没有可变参模板,C++11有了可变参模板就简单多了,可以如下定义IsFunction:

template< typename T >
struct IsFunction
{
    enum { value = 0 };
};

template< typename T, typename... U >
struct IsFunction< T( U... ) >
{
    enum { value = 1 };
};
unituniverse2 2014-01-05
  • 打赏
  • 举报
回复
至于非标量的识别,也是利用SFINAE。这里的代码我就直接照搬以前写过的。简单的看,就是测试类型是否能包含成员函数(仅是看能不能“添加”,而这个函数不一定非得存在):

	// If the input template argument is a type that allows to be supplemented a function to, like did to a structure or a class or a
	//  union, the result will be defined as non-zero.
	template < typename _T_ >
	struct Is_Single_Non_Scalar_Object
	{
	private:
		template < typename _t_ >
		static char (& GetRes(void (_t_::*)(void)))[2];

		template < typename _t_ >
		static char GetRes(...);

	public:
		enum nRes { result = static_cast<int>(sizeof(GetRes<_T_>(nullptr))) - 1 };

		Is_Single_Non_Scalar_Object(void) = delete;
		Is_Single_Non_Scalar_Object(const Is_Single_Non_Scalar_Object &) = delete;
	};
数组的识别用普通的型式特化就可以识别出来(估计你很快就想得到,所以代码就不贴了) 然后内嵌一个结构,并特化出一个版本筛选掉数组和非标量等,仅仅留下标量类型进行分析,也就是把楼主的代码都移入结构中的非特化版本部分里去。
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 13 楼 vipcxj 的回复:
你3L是木有错的,9L是你理解错了,别人说的都是1里面的U,那个U本来就是无根之木,既不是一般类型,也不是typename,所以怎么看都是错的。
喔,偶一直以为说的是第1段代码中的1,原来是modified1中的1
unituniverse2 2014-01-05
  • 打赏
  • 举报
回复
gcc下面没问题,vc编译不过(vs2013) 如果是我的话,宁可用楼主第二个版本,也就是把sizeof里面那个IsFunction<T>::去掉,因为虽然语法没问题但也存在可能是识别成了内嵌类的情况(IsFunction<T>里面又内嵌一个template<typename> IsFunction)。 另外补充一下,这个IsFunction还需要继续完善,至少得直接筛选掉非标量类型和数组。非标量类型中还有个抽象类,也是不能构造数组的:


template<typename T>
class IsFunction
{
private:
	typedef struct {} One;
	typedef struct { One a[2]; } Two;
	template<typename U> static One testFunction(...);
	template<typename U> static Two testFunction(U(*)[1]);
public:
	enum { YES = sizeof(IsFunction<T>::template testFunction<T>(nullptr)) == sizeof(One) };
	enum { NO = !YES };
};

void fun() {}

template<typename T>
void checkFun(T *t)
{
	if (IsFunction<T>::YES)
		std::cout << "is function!" << std::endl;
	else
		std::cout << "is not function!" << std::endl;
}

class A
{
public:
	virtual ~A() = 0;
};


int main(void)
{
	auto myFun = &fun;
	int *pa = nullptr;
	A * pb = nullptr;

	checkFun(pa); // <span style="color: #FF0000;">{1}</span>
	checkFun(pb); // <span style="color: #FF0000;">{1}</span>
	checkFun(myFun); // <span style="color: #FF0000;">{2}</span>
	//IsFunction<cl>::YES;

	_getch();
	return(0);
}
输出结果:

C:\MinGW\bin>test
is not function!
is function!
is function!
hooked 2014-01-05
  • 打赏
  • 举报
回复
引用 3 楼 supermegaboy 的回复:
[quote=引用 楼主 bbs2241 的回复:] 最近看了下C++ templates,其中有个判断类型的tool, Original如下:
template<typename T>
class IsFunction
{
private:
	typedef struct {} One;
	typedef struct { One a[2]; } Two;
	template<typename U> static One testFunction(...);
	template<typename U> static Two testFunction(U (*)[1] );
public:
	enum { YES = sizeof(IsFunction<T>::testFunction<T>(0)) == sizeof(One) };
	enum { NO = !YES };
};
测试程序如下:
void fun() {}

template<typename T>
void checkFun(T *t)
{
	if(IsFunction<T>::YES)
		cout << "is function!" << endl;
	else
		cout << "is not function!" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
	Fun myFun = &fun;
	int *pa;
	checkFun(pa); // {1}
	checkFun(myFun); // {2}
	//IsFunction<cl>::YES;
	getchar();
	return 0;
}
OK这段代码的意思我也明白,主要用了数组和函数重载的一些知识。这段代码是不能编译的,必须做修改。 Modified 1:
template<typename T>
class IsFunction
{
private:
	typedef struct {} One;
	typedef struct { One a[2]; } Two;
	static One testFunction(...);
	static Two testFunction(U (*)[1] );
public:
	enum { YES = sizeof(IsFunction<T>::testFunction(0)) == sizeof(One) };
	enum { NO = !YES };
};
测试结果:不能编译 error:'abstract declarator' array element type cannot be function Modified 2:
template<typename T>
class IsFunction
{
private:
	typedef struct {} One;
	typedef struct { One a[2]; } Two;
	template<typename U> static One testFunction(...);
	template<typename U> static Two testFunction(U (*)[1] );
public:
	enum { YES = sizeof(testFunction<T>(0)) == sizeof(One) };
	enum { NO = !YES };
};
测试结果:OK Modified 3: 将
	typedef struct {} One;
	typedef struct { One a[2]; } Two;
	template<typename U> static One testFunction(...);
	template<typename U> static Two testFunction(U (*)[1] );
放到class外边,测试结果与2相同。 谁能帮解释下,我觉得1不能通过编译的原因是没有使用重载解析
不是的。问题在于VS出现了非标准行为,按照C++标准,testFunction在IsFunction内部无论是否qualified-id都是允许的,但VS却对qualified-id的情况报了错,这是不符合标准的行为。 事实上,VS的非标准行为比较多,相较而言,g++比VS做得好得多。你可以在g++下测试一下。[/quote] 你确定qualified-id在类的内部也是符合标准的么?那试试gcc编译下面的代码

class A
{
public:
    void A::Foo()
    {
    }
};
bobo_包子 2014-01-05
  • 打赏
  • 举报
回复
引用 11 楼 vipcxj 的回复:
引用 10 楼 vipcxj 的回复:
[quote=引用 9 楼 supermegaboy 的回复:] [quote=引用 8 楼 vipcxj 的回复:] [quote=引用 5 楼 supermegaboy 的回复:] [quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。 [/quote] 你再仔细看看吧,modify1中用的是IsFunction<T>::testFunction(0)哦 还有个问题,LZ所谓能通过的原版,我用vs2012也压根不能编译通过 而你给出的理由也完全不靠谱,LZ出现的错误在于使用了函数数组而非函数指针数组,和你说的qualified-id毫无关系。我知道你说qualified-id是指::testFunction,对不?[/quote] 好吧,我看漏了,LZ也说原版不能编译通过~ 不过那个U确实有问题滴,不过LZ也说是笔误了[/quote] 这个是笔误 是我自己修改的。 modified 1是不能测试函数的,测试类型的时候是可以的。 测试函数时的error:'abstract declarator' array element type cannot be function 这个错误应该是不能或者没有使用重载版本吧,应该使用static One testFunction(...),但是编译器使用static Two testFunction(U (*)[1] ); 我的理解是编译器在这里不能使用SFINE原则,不知道我的理解对不对。 我就是对这里比较好奇,我也觉得应该是这样。你们可以发表下观点不?
vipcxj 2014-01-05
  • 打赏
  • 举报
回复
引用 12 楼 supermegaboy 的回复:
引用 11 楼 vipcxj 的回复:
[quote=引用 10 楼 vipcxj 的回复:] [quote=引用 9 楼 supermegaboy 的回复:] [quote=引用 8 楼 vipcxj 的回复:] [quote=引用 5 楼 supermegaboy 的回复:] [quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。 [/quote] 你再仔细看看吧,modify1中用的是IsFunction<T>::testFunction(0)哦 还有个问题,LZ所谓能通过的原版,我用vs2012也压根不能编译通过 而你给出的理由也完全不靠谱,LZ出现的错误在于使用了函数数组而非函数指针数组,和你说的qualified-id毫无关系。我知道你说qualified-id是指::testFunction,对不?[/quote] 好吧,我看漏了,LZ也说原版不能编译通过~ 不过那个U确实有问题滴,不过LZ也说是笔误了[/quote] 编译不通过的问题,请重看一次3楼。关于U的问题,请重新理解一次9楼。[/quote] 你3L是木有错的,9L是你理解错了,别人说的都是1里面的U,那个U本来就是无根之木,既不是一般类型,也不是typename,所以怎么看都是错的。
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 11 楼 vipcxj 的回复:
引用 10 楼 vipcxj 的回复:
[quote=引用 9 楼 supermegaboy 的回复:] [quote=引用 8 楼 vipcxj 的回复:] [quote=引用 5 楼 supermegaboy 的回复:] [quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。 [/quote] 你再仔细看看吧,modify1中用的是IsFunction<T>::testFunction(0)哦 还有个问题,LZ所谓能通过的原版,我用vs2012也压根不能编译通过 而你给出的理由也完全不靠谱,LZ出现的错误在于使用了函数数组而非函数指针数组,和你说的qualified-id毫无关系。我知道你说qualified-id是指::testFunction,对不?[/quote] 好吧,我看漏了,LZ也说原版不能编译通过~ 不过那个U确实有问题滴,不过LZ也说是笔误了[/quote] 编译不通过的问题,请重看一次3楼。关于U的问题,请重新理解一次9楼。
vipcxj 2014-01-05
  • 打赏
  • 举报
回复
引用 10 楼 vipcxj 的回复:
引用 9 楼 supermegaboy 的回复:
[quote=引用 8 楼 vipcxj 的回复:] [quote=引用 5 楼 supermegaboy 的回复:] [quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。 [/quote] 你再仔细看看吧,modify1中用的是IsFunction<T>::testFunction(0)哦 还有个问题,LZ所谓能通过的原版,我用vs2012也压根不能编译通过 而你给出的理由也完全不靠谱,LZ出现的错误在于使用了函数数组而非函数指针数组,和你说的qualified-id毫无关系。我知道你说qualified-id是指::testFunction,对不?[/quote] 好吧,我看漏了,LZ也说原版不能编译通过~ 不过那个U确实有问题滴,不过LZ也说是笔误了
vipcxj 2014-01-05
  • 打赏
  • 举报
回复
引用 9 楼 supermegaboy 的回复:
引用 8 楼 vipcxj 的回复:
[quote=引用 5 楼 supermegaboy 的回复:] [quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。 [/quote] 你再仔细看看吧,modify1中用的是IsFunction<T>::testFunction(0)哦 还有个问题,LZ所谓能通过的原版,我用vs2012也压根不能编译通过 而你给出的理由也完全不靠谱,LZ出现的错误在于使用了函数数组而非函数指针数组,和你说的qualified-id毫无关系。我知道你说qualified-id是指::testFunction,对不?
飞天御剑流 2014-01-05
  • 打赏
  • 举报
回复
引用 8 楼 vipcxj 的回复:
引用 5 楼 supermegaboy 的回复:
[quote=引用 4 楼 bbs2241 的回复:] [quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型[/quote] U在IsFunction<T>::testFunction<T>(0)中被T显式指定了,当T是函数类型时,由于函数不是对象,因而不存在函数数组,使得U (*)[1]是错误的,根据SFINAE,这时候重载解析的结果就是testFunction(...)了,因此U是正确的。
vipcxj 2014-01-05
  • 打赏
  • 举报
回复
引用 5 楼 supermegaboy 的回复:
引用 4 楼 bbs2241 的回复:
[quote=引用 2 楼 Adol1111 的回复:] [quote=引用 1 楼 Automation_dmu 的回复:] 1中 U类型 哪里来?
+1
没修改好,1中的是T[/quote] 那个U并不是错误,是正确的。[/quote] U怎么看都不像是正确滴,最多编译器对模板的检查不怎么严格,但绝对不会是正确的,因为压根没这个类型
bobo_包子 2014-01-05
  • 打赏
  • 举报
回复
好吧 thanks
加载更多回复(4)

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧