以下代码在Visual Studio下编译得很好,但gcc 4.6.2或4.7都无法处理它。它似乎是有效的,但gcc似乎无法解决const和非const参数之间的区别。这可能是编译器错误吗?
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]
答案 0 :(得分:4)
这对我来说看起来像编译器错误。这里涉及的问题是重载解析和模板函数的部分排序。由于两个模板函数都可以匹配参数列表(ReadPackager*, bool)
,因此应该使用模板函数的部分排序来选择更专业的模板函数。
简单地说,如果该函数的参数始终可以用作另一个函数的参数,那么模板函数至少与另一个函数一样专门。
很明显,任何两个指针参数都匹配第一个Package()函数,但是例如Package(ReadPackager *,const int *)与第二个不匹配。这似乎暗示第二个Package函数更专业,应该解决任何歧义。
然而,由于编制者之间存在分歧,可能会有一些细微之处被简化的解释所忽视。因此,我将遵循从标准中确定功能模板部分排序的程序,以辨别正确的行为。
首先,将功能标记为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
U
合成唯一类型Packager
。P1
的参数列表执行类型扣除。 Packager
推断为U
,T
推断为AddPkgrConst<Packager,U>::type
。这成功,P1被判断为不比P2更专业。
现在P1-> P2:
U1
和U2
合成唯一类型Packager
和T
,以获取参数列表(U1 *,U2 *)。P2
的参数列表执行类型扣除。 Packager
被推断为U1。AddPkgrConst<U1,bool>::type
,其评估为bool
。这与第二个参数U2
不匹配。如果我们继续执行第4步,此过程将失败。但是,我怀疑拒绝此代码的编译器不执行步骤4,因此仅仅因为类型推导成功而认为P2不再比P1更专业。这似乎是反直觉的,因为P1明确接受P2所做的任何输入,反之亦然。标准的这一部分有点复杂,因此不清楚是否需要进行最终比较。
让我们尝试通过应用§14.8.2.5,第1段,从类型
中扣除模板参数来解决这个问题模板参数可以在几个不同的上下文中推导出来,但是在每种情况下,根据模板参数(称为P)指定的类型与实际类型(称为A)进行比较,并尝试查找模板参数值(类型参数的类型,非类型参数的值或模板参数的模板),在替换推导出的值(称为推导出的A)之后,将生成P,与之兼容甲
在我们的类型演绎中,推导出的A为AddPkgrConst<U1,bool>::type
= bool
。这与原始A不兼容,原始A是唯一类型U2
。这似乎支持了部分排序解决模糊性的立场。
答案 1 :(得分:3)
我不知道Visual Studio有什么问题,但gcc
说的似乎是对的:
您实例化AddPkgrConstByType<CReadType, T>
因为Packager::CReadWriteType
已解析为CReadType
。因此,AddPkgrConst<Packager,bool>::type
将根据bool
的第一个实现(不是专业化)来解决。这意味着您有两个具有相同参数列表的独立函数特化,C ++不允许这样做。
答案 2 :(得分:1)
由于函数模板不能专门化,这里你有两个函数模板重载。这两个重载都能够接受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.
}