6

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 ?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Jagannath
  • 3,995
  • 26
  • 30
  • 1
    Does removing the const maybe workarodun it? – PlasmaHH Jul 02 '12 at 15:21
  • Yes. Removing the const or adding const to int arr[] is the work around. But, would like to know if this is the bug in the compiler or my understanding is wrong. – Jagannath Jul 02 '12 at 15:22
  • 2
    Chances are if latest versions of clang,gcc, comeau and msvc agree, then it is a bug in SunCC. Also note that SunCC is (in)famous for being buggy. Which leads to the question: why not use gcc? – PlasmaHH Jul 02 '12 at 15:33
  • 1
    I hope you agree with me that there are far too many dependencies to switch the vendors. I am just a screw in a very big machine :-) – Jagannath Jul 02 '12 at 15:37
  • I believe it's a bug. Otherwise how could an identical syntax with generic version could compile ? If I remove the specialize version and only have the generic version then still the code compiles. – Jagannath Jul 02 '12 at 15:39
  • It's a compiler bug. There's nothing wrong with your code. – mfontanini Jul 02 '12 at 15:40
  • 1
    @Jagannath: When the code is standard C++, then the amount of possible dependencies should be minimal... if otoh you are using SunCC specific extensions, well... – PlasmaHH Jul 02 '12 at 15:41
  • @PlasmaHH I suspect the OP is using SunCC-specific C++ library binaries. – Mark B Jul 02 '12 at 15:42
  • 1
    cannot agree on that. If using standard, there should be no dependencies. compiles fine with gcc/4.7.0, calling specialised version. – Walter Jul 02 '12 at 15:43
  • @MarkB: Its been a while since I was forced to work on suns, but wasn't gcc able to handle them just fine? – PlasmaHH Jul 02 '12 at 15:45
  • RogueWave Library is extensively used in our codebase. That could be one reason for sticking to Sun. As I said earlier I am just a developer in the team. Don't have any authority in decision making. – Jagannath Jul 02 '12 at 15:51
  • OK thank you all. Will take it up further with the team leads. – Jagannath Jul 02 '12 at 15:54
  • @PlasmaHH C libraries sure, but the Sun and g++ ABI for C++ libraries is completely different. – Mark B Jul 02 '12 at 15:54
  • Can you post the full error message? – David Rodríguez - dribeas Jul 02 '12 at 16:04
  • @DavidRodríguez-dribeas I just removed the line number from the error message. That's the complete error message I get. – Jagannath Jul 02 '12 at 16:11
  • 1
    @Jagannah: I see... CC is very concise with error messages. I have tested and removing the `const` from the signature solves it. Now the interesting question is whether it is legal or not to bind a reference of type array of `const T` to an array of `T` (non-const)... – David Rodríguez - dribeas Jul 02 '12 at 16:27
  • CC -library=Cstd +w2 -I. -g -lpthread -o Sample.o -c Sample.cc "Sample.cc", line 65: Error: Could not find a match for LoopThrough(int[2]) needed in main(). 1 Error(s) detected. *** Error code 1 make: Fatal error: Command failed for target `Sample.o' – Jagannath Jul 02 '12 at 16:30
  • Just removed the machine details, etc. Other than that this is what it is. Yes. Removing const solves it. – Jagannath Jul 02 '12 at 16:31
  • ... Also note that there is no *specialized* version in the question. Those are templated functions, for which there is no valid partial specializations. The two are different overloads (two unrelated base templates) – David Rodríguez - dribeas Jul 02 '12 at 16:37
  • Yes. For functions it's just a function overload and not specialization. But still, the one with int as parameter should have been called. – Jagannath Jul 02 '12 at 16:47

2 Answers2

2

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));
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
1

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

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • The OP's problem is specifically that with only the one template it fails to compile because the compiler won't bind a non-const array to const array parameter. – Mark B Jul 25 '12 at 14:51
  • @MarkB Regardless, it's simply good style to have the extra indirection to specialized function objects instead of multiple function overloads. – TemplateRex Jul 26 '12 at 06:03