首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >gcc4模板错误或更可能的id10t错误

gcc4模板错误或更可能的id10t错误
EN

Stack Overflow用户
提问于 2012-01-28 02:06:23
回答 3查看 200关注 0票数 5

下面的代码在Visual Studio下编译得很好,但是gcc 4.6.2和4.7都不能处理它。它似乎是有效的,但gcc似乎不能解决常量参数和非常数参数之间的差异。这可能是一个编译器错误吗?

代码语言:javascript
复制
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 );
}

编译器调用:

代码语言:javascript
复制
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]
EN

回答 3

Stack Overflow用户

发布于 2012-01-28 05:30:49

在我看来,这是一个编译器错误。这里涉及到的问题是重载解析和模板函数的偏序。由于两个模板函数都可以匹配参数列表(ReadPackager*, bool),因此应该使用模板函数的偏序来选择更专业的模板函数。

简而言之,如果一个模板函数的参数总是可以用作另一个模板函数的参数,那么该模板函数至少与另一个模板函数一样专门化。

很明显,任何两个指针参数都与第一个Package()函数匹配,但例如Package(ReadPackager*,const int*)不能匹配第二个函数。这似乎意味着第二个包函数更加专门化,应该可以解决任何歧义。

然而,由于编译器之间存在分歧,简化的解释可能会忽略其中的一些微妙之处。因此,我将遵循从标准中确定函数模板偏序的过程,以识别正确的行为。

首先,将函数标记为P1和P2,以便于引用。

P1:

代码语言:javascript
复制
template<typename Packager, typename T>
bool Package( Packager* ppkgr, T* pt );

P2:

代码语言:javascript
复制
template<typename Packager>
bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb);

标准规定,对于每个模板函数(T1),我们必须为其每个模板参数泛型唯一的类型,使用这些类型来确定函数调用参数类型,然后使用这些类型来推导另一个模板(T2)中的类型。如果此操作成功,则第一个模板(T1)至少与第二个模板(T2)一样专业。第一个P2->P1

这样做成功了,P1被认为并不比P2更专业。

现在P1->P2:

  1. 为模板参数U1U2PackagerT合成唯一类型U1U2,以获得参数列表(U1*,U2*).
  2. Perform类型根据P1的参数列表进行推断。Packager推导为U1。
  3. 不对第二个参数执行推导,因为它是依赖类型,被视为非推导的上下文。
  4. 因此,第二个参数是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类型。这似乎支持了偏序解决歧义的观点。

票数 4
EN

Stack Overflow用户

发布于 2012-01-28 02:22:56

我不知道Visual Studio出了什么问题,但gcc说的似乎是对的:

实例化AddPkgrConstByType<CReadType, T>是因为Packager::CReadWriteType解析为CReadType。因此,AddPkgrConst<Packager,bool>::type将根据第一个实现(不是专门化)解析到bool。这意味着你有两个不同的函数专门化,具有相同的参数列表,这是C++不允许的。

票数 3
EN

Stack Overflow用户

发布于 2012-01-28 02:59:47

因为函数模板不能被专门化,所以这里有两个函数模板重载。这两个重载都能够接受bool*作为它们的第二个参数,因此g++会正确地检测到它们是不明确的。

然而,类可以部分专门化,这样只会选择一个版本,您可以使用包装器魔术来实现您想要的目标。我只是粘贴我添加的代码。

代码语言:javascript
复制
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.
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9037940

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档