【问题标题】:How can I disambiguate this template code?如何消除此模板代码的歧义?
【发布时间】:2009-05-08 21:45:51
【问题描述】:

我无法弄清楚如何解决我遇到的编译器错误。我已将其简化为最简单的表示形式:

enum EAtomId { EAtomId_Test };

int StringFormat(char* o_dest, size_t i_destSizeChars, const char* i_format, ...);

template <size_t SIZE>
int StringFormat(char (&o_dest)[SIZE], EAtomId i_format, ...);

void func()
{
    char textBuffer[1000];
    StringFormat(textBuffer, EAtomId_Test, "hi there");
}

编译错误是:

repro.cpp(17) : error C2666: 'StringFormat' : 2 overloads have similar conversions
    C:\Users\sbilas\Desktop\repro.cpp(9): could be 'int StringFormat(char *,size_t,const char *,...)'
    while trying to match the argument list '(char [1000], EAtomId, const char [9])'

顺便说一句,这是完整的错误。我有点惊讶它没有列出两个可用版本..

我对这个错误有几个问题。首先,我不明白为什么它是模棱两可的。编译器不应该将 char(&)[] 版本视为匹配的明显案例吗?其次,如何防止该枚举在进行查找时转换为 size_t ?似乎我在这里遇到了一些非常具体的 C++ 规则。

对我来说最简单的解决方法是将缓冲区大小的 size_t 放在实际缓冲区的前面。但这将打破我们在代码中放置缓冲区然后大小的所有约定。有没有其他方法可以做到这一点?

这是在 VC++ 2005 顺便说一句,但它在我拥有的其他几个编译器上重现(这是在跨平台游戏中)。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    上述代码的问题是由于普通的重载解析规则,而不是特定于模板。以下带有两个普通函数的代码仍然显示出歧义:

    enum EAtomId { EAtomId_Test };
    const int SIZE=1000;
    
    int StringFormat(char * s
      , size_t i_destSizeChars
      , const char* i_format
      , ...);
    
    int StringFormat(char (&a)[SIZE]
      , EAtomId i_format
      , ...);
    
    void func()
    {
        char textBuffer[SIZE];
        StringFormat (textBuffer, EAtomId_Test, "hi there");
    }
    

    重载解析的适用规则如下(13.3.3):

    • 对于每组参数,计算出发生的一组转换
    • 最好的函数是一个函数,它的所有转换都至少与所有其他重载中的转换一样好。

    依次考虑这些:

    功能一:

    1. textBuffer->s:指向指针的数组(精确匹配 13.3.3.1.1/3)- 标识
    2. EAtomId_Test->i_destSizeChars:可能的提升和积分转换
    3. 字符串文字->const char*:指向指针的数组

    功能2:

    1. textBuffer->a:身份转换
    2. EAtomId_Test->EAtomId:身份转换
    3. 字符串文字->省略号:省略号转换

    依次比较这些转化次数:

    1. 功能1与功能2相同
    2. 功能 2 优于功能 1
    3. 功能 1 优于功能 2

    因此,函数 1 和函数 2 对于所有转换都没有那么好,因此调用是模棱两可的。

    【讨论】:

    • 感谢您的详细回复。如果我理解正确,在参数 2 中“可能的提升和整体转换”与“身份转换”一样强?我原以为编译器会希望在需要转换才能使其工作的情况下选择身份。除了重新排序之外,您能否提出解决此问题的方法?我想要做的是为静态大小的字符串缓冲区获得自动长度安全。同时我们有两种方法来指定格式字符串——通过文字和通过“原子”(从字典中查找以进行本地化)。
    【解决方案2】:

    StringFormat 有两个声明:

    int StringFormat(char* o_dest, size_t i_destSizeChars,
                     const char* i_format, ...);
    
    template <size_t SIZE>
    int StringFormat(char (&o_dest)[SIZE], EAtomId i_format, ...);
    

    当函数模板和普通(非模板)函数都可用时,请阅读与参数相关的查找和替换规则。

    【讨论】:

    • 这些都不适用。 ADL 在这里并不真正适用,因为它只添加到查找集,但是这两个功能对“正常查找”都是可见的。函数模板和非模板函数的消歧仅适用于两种函数类型相同的情况。 (13.3.3/1)。
    • @Richard Corden:我打算解决重载问题。这些不是为了点,而是为了完整性。我点错了——我应该把我最后说的放在前面,然后加上关于 ADL 的点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 2010-09-08
    • 2019-01-29
    • 1970-01-01
    相关资源
    最近更新 更多