下面的代码在Visual Studio下编译得很好,但是gcc 4.6.2和4.7都不能处理它。它似乎是有效的,但gcc似乎不能解决常量参数和非常数参数之间的差异。这可能是一个编译器错误吗?
struct CReadType{};
struct CWriteType{};
template<typename ReadWriteType, typename T>
struct AddPkgrConstByType {};
template<typename T>
struct AddPkgrConstByType<CReadType, T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CReadType, const T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CWriteType, T> {
typedef T const type;
};
template<typename Packager, typename T>
struct AddPkgrConst : public AddPkgrConstByType<typename Packager::CReadWriteType, T> {
};
template<typename Packager, typename T>
inline bool Package( Packager* ppkgr, T* pt )
{
return true;
}
template<typename Packager>
inline bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb)
{
return false;
}
struct ReadPackager {
typedef CReadType CReadWriteType;
};
struct WritePackager {
typedef CWriteType CReadWriteType;
};
int main(int argc, char* argv[])
{
ReadPackager rp;
WritePackager wp;
bool b = true;
const bool cb = false;
Package( &rp, &b );
}编译器调用:
g++ -fPIC -O -std=c++0x -Wno-deprecated -D_REENTRANT
g++-D__STDC_LIMIT_MACROS -c test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:58:22: error: call of overloaded ‘Package(ReadPackager*, bool*)’ is ambiguous
test.cpp:58:22: note: candidates are:
test.cpp:31:6: note: bool Package(Packager*, T*) [with Packager = ReadPackager, T = bool]
test.cpp:38:6: note: bool Package(Packager*, typename AddPkgrConst<Packager, bool>::type*) [with Packager = ReadPackager, typename AddPkgrConst<Packager, bool>::type = bool]发布于 2012-01-28 05:30:49
在我看来,这是一个编译器错误。这里涉及到的问题是重载解析和模板函数的偏序。由于两个模板函数都可以匹配参数列表(ReadPackager*, bool),因此应该使用模板函数的偏序来选择更专业的模板函数。
简而言之,如果一个模板函数的参数总是可以用作另一个模板函数的参数,那么该模板函数至少与另一个模板函数一样专门化。
很明显,任何两个指针参数都与第一个Package()函数匹配,但例如Package(ReadPackager*,const int*)不能匹配第二个函数。这似乎意味着第二个包函数更加专门化,应该可以解决任何歧义。
然而,由于编译器之间存在分歧,简化的解释可能会忽略其中的一些微妙之处。因此,我将遵循从标准中确定函数模板偏序的过程,以识别正确的行为。
首先,将函数标记为P1和P2,以便于引用。
P1:
template<typename Packager, typename T>
bool Package( Packager* ppkgr, T* pt );P2:
template<typename Packager>
bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb);标准规定,对于每个模板函数(T1),我们必须为其每个模板参数泛型唯一的类型,使用这些类型来确定函数调用参数类型,然后使用这些类型来推导另一个模板(T2)中的类型。如果此操作成功,则第一个模板(T1)至少与第二个模板(T2)一样专业。第一个P2->P1
这样做成功了,P1被认为并不比P2更专业。
现在P1->P2:
U1和U2的Packager和T合成唯一类型U1和U2,以获得参数列表(U1*,U2*).Packager推导为U1。AddPkgrConst<U1,bool>::type,其计算结果为bool。这与第二个参数code不匹配如果我们继续执行步骤4,这个过程就会失败。然而,我怀疑拒绝此代码的编译器不会执行步骤4,因此仅仅因为类型推导成功,就认为P2并不比P1更专业。这似乎有悖于直觉,因为P1清楚地接受P2所接受的任何输入,反之亦然。标准的这一部分有些令人费解,因此不清楚是否需要进行最终比较。
让我们尝试通过应用§14.8.2.5,第1段,从类型中推导模板参数来解决这个问题
模板参数可以在几种不同的上下文中推导,但在每种情况下,都会将根据模板参数指定的类型(称为P)与实际类型(称为A)进行比较,并尝试找到模板参数值(类型参数的类型、非类型参数的值或模板参数的模板),这些值将使P在替换推导的值(称为推导的A)后与A兼容。
在我们的类型推导中,推导出的A是AddPkgrConst<U1,bool>::type=bool。这与原始的A不兼容,A是唯一的U2类型。这似乎支持了偏序解决歧义的观点。
发布于 2012-01-28 02:22:56
我不知道Visual Studio出了什么问题,但gcc说的似乎是对的:
实例化AddPkgrConstByType<CReadType, T>是因为Packager::CReadWriteType解析为CReadType。因此,AddPkgrConst<Packager,bool>::type将根据第一个实现(不是专门化)解析到bool。这意味着你有两个不同的函数专门化,具有相同的参数列表,这是C++不允许的。
发布于 2012-01-28 02:59:47
因为函数模板不能被专门化,所以这里有两个函数模板重载。这两个重载都能够接受bool*作为它们的第二个参数,因此g++会正确地检测到它们是不明确的。
然而,类可以部分专门化,这样只会选择一个版本,您可以使用包装器魔术来实现您想要的目标。我只是粘贴我添加的代码。
template <typename Packager, typename T>
struct Wrapper
{
static bool Package()
{
return true;
}
};
template <typename Packager>
struct Wrapper<Packager, typename AddPkgrConst<Packager,bool>::type>
{
static bool Package()
{
return false;
}
};
template <typename Packager, typename T>
Wrapper<Packager, T> make_wrapper(Packager* /*p*/, T* /*t*/)
{
return Wrapper<Packager, T>();
}
int main()
{
ReadPackager rp;
bool b = true;
std::cout << make_wrapper(&rp, &b).Package() << std::endl; // Prints out 0.
}https://stackoverflow.com/questions/9037940
复制相似问题