gcc4模板错误或更可能是id10t错误

时间:2012-01-27 18:06:23

标签: c++ templates gcc compiler-construction gcc4

以下代码在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]

3 个答案:

答案 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

  1. 为P2的模板参数U合成唯一类型Packager
  2. P1的参数列表执行类型扣除。 Packager推断为UT推断为AddPkgrConst<Packager,U>::type
  3. 这成功,P1被判断为不比P2更专业。

    现在P1-> P2:

    1. 为P1的模板参数U1U2合成唯一类型PackagerT,以获取参数列表(U1 *,U2 *)。
    2. P2的参数列表执行类型扣除。 Packager被推断为U1。
    3. 不会对第二个参数执行任何推论,因为作为从属类型,它被视为非推断的上下文。
    4. 因此,第二个参数为AddPkgrConst<U1,bool>::type,其评估为bool。这与第二个参数U2不匹配。
    5. 如果我们继续执行第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.
}