Question

We are migrating to Sun Studio 12.1 and with the new compiler [ CC: Sun C++ 5.10 SunOS_sparc 2009/06/03 ]. I am getting compilation error while compiling a code that compiled fine with earlier version of Sun Compiler [ CC: Sun WorkShop 6 update 2 C++ 5.3 2001/05/15 ].

This is the compilation error I get.

"Sample.cc": Error: Could not find a match for LoopThrough(int[2]) needed in main(). 1 Error(s) detected. *** Error code 1.

CODE:

#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);    
    }
}

If I uncomment the code with Generic version, the code compiles fine and the generic version is called. I don't see this problem with MSVC 2010 with extensions disabled and the same case with ideone here. The specialized version of the function is called. Now the question is, is this a bug in Sun Compiler ?

If yes, how could we file a bug report ?

Was it helpful?

Solution

The compiler is not following the standard in this case and is buggy. Let's review the relevant sections.

First from 13.3/3 we have:

...

— First, a subset of the candidate functions—those that have the proper number of arguments and meet certain other conditions—is selected to form a set of viable functions (13.3.2).

— Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

So both functions have the same number of arguments and are considered candidates. Now we have to find the best viable function, in

13.3.3:

let ICSi(F) denote the implicit conversion sequence that converts the ith argument in the list to the type of the ith parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another

Then we have

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

— F1 is a nontemplate function and F2 is a template function specialization, or, if not that,

— F1 and F2 are template functions, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2, or, if not that,

The two functions are equal for the first rule (adding const), and the second rule doesn't apply (both are templates). So we move to the third rule. From 14.5.5.2 (which I will quote if requested) we learn that the const int version of the function is more specialized than the const Item version, and so the best match is the const int overload, which should then be called.

Your best temporary fix is probably a second overload:

template<size_t SZ>
void LoopThrough(int (&Item)[SZ])
{
    LoopThrough(static_cast<const int (&)[SZ]>(Item));
}

OTHER TIPS

Your compiler is buggy. Both overloads have their template arguments deduced, and overload resolution should select the most specialized one. So apart from getting a new compiler, what can you do?

First, it's helpful to realize that -even with conforming compilers- it is generally not a good a idea to have different function template overloads. See e.g. Item 66 of C++ Coding Standards: 101 Rules, Guidelines, and Best Practices by Herb Sutter and Andrei Alexandrescu.

Fortunately, that Item also suggests a possible fix. All you have to do is define a single function template and let that function template delegate the work to a class template function object. You can then partially specialize this class template for 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);    
    }
}

Most likely, the compiler will inline the call to the function object and completely optimize away the temporary. Hopefully your compiler will also have correctly implemented partial specialization of class templates.

Output on Ideone

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top