Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguity in variadic function template

I recently started trying out various template tricks. In this case I tried to implement a class holding a sequence of numbers supplied by a variadic template utilizing a parameter pack.

However, I run into some problems that do not occur on the compiler I use (Intel C++ 14), but on others like CLang or GCC. I am therefore quite confused about which compiler is "more on point" in its interpretation of the standard.

Here is my code:

#include <iostream>

using namespace std;

template< size_t... Sequence >
class CSequence
{ 
    public:
        CSequence()
        {
            this->generate< Sequence... >();
            this->out();
        }

    private:
        // Recursion end
        template< size_t N >
        void generate(size_t sz)
        {
            // Create array
            this->m_Array = new size_t[sz+1];

            this->m_Len = sz+1;

            this->m_Array[sz] = N;
        }

        // Recursion segment
        template< size_t N, size_t... Ns >
        void generate(size_t sz)
        {
            generate<Ns...>(sz+1);

            this->m_Array[sz] = N;
        }

        // Recursion start
        template< size_t... Ns >
        void generate()
        {
            generate<Ns...>(0);
        }         

        void out()
        {
            for(int i = 0; i < this->m_Len; i++)
            {
                std::cout << this->m_Array[i] << " ";
            }

            std::cout << std::endl;
        }

    private:
        size_t*     m_Array;
        size_t      m_Len;
};

int main()
{
    CSequence< 1, 2, 3, 4, 5, 6, 7, 8 > a;
   std::getchar();
}

This compiles fine on my end using Intel C++ 14 and produces the result I would expect:

1 2 3 4 5 6 7 8 

But on the newest versions of both CLang and GCC it fails to compile:

  • CLang @ wandbox Online compiler
  • GCC @ wandbox Online compiler

Now, I can actually understand the reason for the failure: As parameter packs can contain zero elements, the exemplary call

generate< 8 >( size_t );

may be resolved both as

generate< N = 8, Ns = <> > ( size_t )

and as

generate< N = 8 > ( size_t )

thus resulting in an unresolvable ambiguity. But the fact that it compiles on my end and actually gives the expected result makes me wonder:

  1. Which compiler is "right"? Obviously Intel C++ makes some additional decisions compared to CLang or GCC. Is this "illegal" concerning the standard? Or are the GCC/CLang simply lacking?
  2. Are there any ways to circumvent this behaviour?

Note that this piece of code is to be considered as some kind of experiment, so I'm happy with both alternative strategies and fixes for this particular piece of code.

like image 320
nshct Avatar asked Sep 27 '22 18:09

nshct


1 Answers

This doesn't require a recursive implementation at all.

template< size_t... Ns >
void generate()
{
    m_Len = sizeof...(Ns);
    m_Array = new size_t[m_Len] { Ns... };
}

You can also dispense with the dynamic allocation and make m_Array an actual array, and initialize it with a NSDMI:

size_t m_Array[sizeof...(Sequence)] = {Sequence... };
like image 120
T.C. Avatar answered Sep 30 '22 07:09

T.C.