6

我们正在迁移到 Sun Studio 12.1 和新的编译器 [CC: Sun C++ 5.10 SunOS_sparc 2009/06/03]。我在编译使用早期版本的 Sun 编译器 [CC: Sun WorkShop 6 update 2 C++ 5.3 2001/05/15] 编译良好的代码时遇到编译错误。

这是我得到的编译错误。

“Sample.cc”:错误:找不到 main() 中所需的 LoopThrough(int[2]) 的匹配项。1 检测到错误。*** 错误代码 1。

代码:

#include <iostream> 

#define PRINT_TRACE(STR) \
std::cout << __FILE__ << ":" << __LINE__ << ":" << STR << "\n";

template<size_t SZ>
void LoopThrough(const int(&Item)[SZ])
{
    PRINT_TRACE("Specialized version");
    for (size_t index = 0; index < SZ; ++index)
    {
        std::cout << Item[index] << "\n";
    }
}


/*     
    template<typename Type, size_t SZ>
    void LoopThrough(const Type(&Item)[SZ])
    {
        PRINT_TRACE("Generic version");        
    }
 */  



int main()
{
    {
       int arr[] = { 1, 2 };
       LoopThrough(arr);    
    }
}

如果我用通用版本取消注释代码,代码编译得很好并且通用版本被调用。在禁用扩展的 MSVC 2010 和这里的 ideone 情况下,我没有看到这个问题。调用该函数的专用版本。现在的问题是,这是 Sun Compiler 中的错误吗?

如果是,我们如何提交错误报告?

4

2 回答 2

2

在这种情况下,编译器没有遵循标准并且有问题。让我们回顾一下相关部分。

首先从 13.3/3 我们有:

...

— 首先,候选函数的子集——具有适当数量的参数并满足某些其他条件的函数——被选择以形成一组可行的函数(13.3.2)。

— 然后根据将每个参数与每个可行函数的相应参数匹配所需的隐式转换序列 (13.3.3.1) 选择最佳可行函数。

因此,这两个函数具有相同数量的参数,并被视为候选函数。现在我们必须找到最好的可行函数,在

13.3.3:

令 ICSi(F) 表示将列表中的第 i 个参数转换为可行函数 F 的第 i 个参数的类型的隐式转换序列。13.3.3.1 定义了隐式转换序列,13.3.3.2 定义了一次隐式转换的含义序列是比另一个更好的转换序列或更差的转换序列

然后我们有

鉴于这些定义,如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2) 更差的转换序列,则可行函数 F1 被定义为比另一个可行函数 F2 更好的函数,然后

— 对于某些参数 j,ICSj(F1) 是比 ICSj(F2) 更好的转换序列,或者,如果不是,

— F1 是非模板函数,F2 是模板函数特化,或者,如果不是,

— F1 和 F2 是模板函数,根据 14.5.5.2 中描述的偏序规则,F1 的函数模板比​​ F2 的模板更专业,或者,如果不是,

对于第一条规则,这两个函数是相等的(添加 const),而第二条规则不适用(都是模板)。所以我们转向第三条规则。从 14.5.5.2(如果需要,我会引用)我们了解到const int函数的版本比const Item版本更专业,因此最好的匹配是const int重载,然后应该调用它。

您最好的临时修复可能是第二次重载:

template<size_t SZ>
void LoopThrough(int (&Item)[SZ])
{
    LoopThrough(static_cast<const int (&)[SZ]>(Item));
}
于 2012-09-26T21:06:42.767 回答
1

你的编译器有问题。两种重载都推导出了它们的模板参数,重载决议应该选择最专业的一个。所以除了获得一个新的编译器,你还能做什么?

首先,认识到 - 即使使用符合标准的编译器 - 使用不同的函数模板重载通常也不是一个好主意,这很有帮助。例如,参见Herb Sutter 和 Andrei Alexandrescu的C++ 编码标准:101 条规则、指南和最佳实践的第 66 条。

幸运的是,该项目还提出了一个可能的解决方案。您所要做的就是定义一个函数模板,并让该函数模板将工作委托给类模板函数对象。然后,您可以将此类模板部分专门化为ints.

#include <iostream> 

#define PRINT_TRACE(STR) \
std::cout << __FILE__ << ":" << __LINE__ << ":" << STR << "\n";

namespace detail {    

// primary template
template<typename Type, size_t SZ>
class LoopThroughHelper
{
public:
    void operator()(const Type(&Item)[SZ]) 
    {
        PRINT_TRACE("Generic version");        
    }
}; 

// partial specialization for int arrays
template<size_t SZ>
class LoopThroughHelper<int, SZ>
{
public:
    void operator()(const int(&Item)[SZ]) 
    {
        PRINT_TRACE("Specialized version");
        for (size_t index = 0; index < SZ; ++index)
        {
            std::cout << Item[index] << "\n";
        }
    }
}; 

} // namespace detail

// one function template to rule them all
template<typename Type, size_t SZ>
void LoopThrough(const Type(&Item)[SZ])
{
     detail::LoopThroughHelper<Type, SZ>()(Item);        
}

int main()
{
    {
       int arr[] = { 1, 2 };
       LoopThrough(arr);    
    }
}

最有可能的是,编译器将内联对函数对象的调用并完全优化掉临时对象。希望您的编译器也将正确实现类模板的部分特化。

Ideone上输出

于 2012-07-21T16:48:43.860 回答