Problem with calling a variadic function template when passing brace initialiser list arguments












14














Consider this function template:



template <class... T>
void foo (std::tuple<T, char, double> ... x);


This invocation works:



using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );


This one doesn't:



foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );


(gcc and clang both complain about too many arguments for foo)



Why is the second call a problem? Can I rewrite the declaration of foo so that the second call is also accepted?



Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double, this is just an example.



I cannot use C++17 for this. A C++11-compatible solution is much preferred.










share|improve this question




















  • 2




    this, this or this
    – Piotr Skotnicki
    Dec 9 at 12:45






  • 1




    @PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
    – JeJo
    Dec 9 at 12:52






  • 1




    With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
    – max66
    Dec 9 at 13:13






  • 1




    @n.m. then what's wrong with providing a sufficient number of overloads like here ?
    – Piotr Skotnicki
    Dec 9 at 13:20






  • 1




    My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
    – max66
    Dec 9 at 13:20


















14














Consider this function template:



template <class... T>
void foo (std::tuple<T, char, double> ... x);


This invocation works:



using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );


This one doesn't:



foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );


(gcc and clang both complain about too many arguments for foo)



Why is the second call a problem? Can I rewrite the declaration of foo so that the second call is also accepted?



Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double, this is just an example.



I cannot use C++17 for this. A C++11-compatible solution is much preferred.










share|improve this question




















  • 2




    this, this or this
    – Piotr Skotnicki
    Dec 9 at 12:45






  • 1




    @PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
    – JeJo
    Dec 9 at 12:52






  • 1




    With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
    – max66
    Dec 9 at 13:13






  • 1




    @n.m. then what's wrong with providing a sufficient number of overloads like here ?
    – Piotr Skotnicki
    Dec 9 at 13:20






  • 1




    My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
    – max66
    Dec 9 at 13:20
















14












14








14


3





Consider this function template:



template <class... T>
void foo (std::tuple<T, char, double> ... x);


This invocation works:



using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );


This one doesn't:



foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );


(gcc and clang both complain about too many arguments for foo)



Why is the second call a problem? Can I rewrite the declaration of foo so that the second call is also accepted?



Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double, this is just an example.



I cannot use C++17 for this. A C++11-compatible solution is much preferred.










share|improve this question















Consider this function template:



template <class... T>
void foo (std::tuple<T, char, double> ... x);


This invocation works:



using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );


This one doesn't:



foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );


(gcc and clang both complain about too many arguments for foo)



Why is the second call a problem? Can I rewrite the declaration of foo so that the second call is also accepted?



Thee template parameter T is only used to implement variadicity. The actual type is known and fixed, only the number of arguments varies. In real life the types are different from int, char, double, this is just an example.



I cannot use C++17 for this. A C++11-compatible solution is much preferred.







c++ c++11 variadic-templates brace-initialization






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 9 at 13:09

























asked Dec 9 at 11:29









n.m.

71k882166




71k882166








  • 2




    this, this or this
    – Piotr Skotnicki
    Dec 9 at 12:45






  • 1




    @PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
    – JeJo
    Dec 9 at 12:52






  • 1




    With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
    – max66
    Dec 9 at 13:13






  • 1




    @n.m. then what's wrong with providing a sufficient number of overloads like here ?
    – Piotr Skotnicki
    Dec 9 at 13:20






  • 1




    My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
    – max66
    Dec 9 at 13:20
















  • 2




    this, this or this
    – Piotr Skotnicki
    Dec 9 at 12:45






  • 1




    @PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
    – JeJo
    Dec 9 at 12:52






  • 1




    With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
    – max66
    Dec 9 at 13:13






  • 1




    @n.m. then what's wrong with providing a sufficient number of overloads like here ?
    – Piotr Skotnicki
    Dec 9 at 13:20






  • 1




    My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
    – max66
    Dec 9 at 13:20










2




2




this, this or this
– Piotr Skotnicki
Dec 9 at 12:45




this, this or this
– Piotr Skotnicki
Dec 9 at 12:45




1




1




@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52




@PiotrSkotnicki That looks promising for the OP's requirement(C++11). Then why you don't paste them as an answer? (Just curious)
– JeJo
Dec 9 at 12:52




1




1




With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
– max66
Dec 9 at 13:13




With different types in it, {1, '2', 3.0} can't be deduced as std::initializer_list or C-style array; and can't be deduced as std::tuple<T, char, double> because {1, 2, 3.0} itself isn't a std::tuple. I suppose you have to use K, or explicit the type calling foo() (so foo<int>( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );) or avoid the braces, at least for the first triplet (so foo(1,'2',3.0, {4,'5',6.0}, {7,'8',9.0})) to permit T deduction.
– max66
Dec 9 at 13:13




1




1




@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20




@n.m. then what's wrong with providing a sufficient number of overloads like here ?
– Piotr Skotnicki
Dec 9 at 13:20




1




1




My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20






My third hypothesis before require and additional couple of braces: foo(1,'2',3.0, {{4,'5',6.0}, {7,'8',9.0}}). So the first 1 is deduced as int and the following triplets as a std::tuple<int, char, double>const [2]
– max66
Dec 9 at 13:20














3 Answers
3






active

oldest

votes


















8














Generate an overloaded set of constructors:



#include <tuple>
#include <cstddef>

template <typename T, std::size_t M>
using indexed = T;

template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;

initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};

template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};

using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^

int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}


DEMO





Generate an overloaded set of function call operators:



#include <tuple>
#include <cstddef>

template <typename T, std::size_t M>
using indexed = T;

template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();

int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};

template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};

static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^

int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}


DEMO 2





Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:



#include <array>
#include <tuple>

using K = std::tuple<int, char, double>;

void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}

void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }

int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}


DEMO 3





Pass as an array and deduce its size (requires additional pair of parens):



#include <tuple>
#include <cstddef>

template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}

int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}


DEMO 4





Use an std::initializer_list as a constructor parameter (to skip extra parens):



#include <tuple>
#include <initializer_list>

struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};

int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}


DEMO 5






share|improve this answer























  • intriguing the first one solution
    – max66
    Dec 9 at 14:25





















4














{} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:



template <class T> struct A { T r; };
template <class T>
void foo (A<T> x);

using K = A<int>;
foo({1}); // fail
foo(K{1}); // compile


This is covered by [temp.deduc.call]/1




If removing references and cv-qualifiers from P gives std::initializer_­list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




and [temp.deduct.type]/5




The non-deduced contexts are:



(5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).




When you:




  • explicitly provide template arguments, it works ... nothing to deduce

  • specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.






share|improve this answer































    0















    I cannot use C++17 for this. A C++11-compatible solution is much preferred.




    With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as



    foo (std::tuple<int, char, double> ... ts)


    and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a



    func ();
    func (K t0);
    func (K t0, K t1);
    func (K t0, K t1, K t2);


    where K is your



    using K = std::tuple<int, char, double>;


    The following is a full compiling C++11 example



    #include <tuple>
    #include <iostream>

    using K = std::tuple<int, char, double>;

    template <typename T, std::size_t>
    struct getTypeStruct
    { using type = T; };

    template <typename T, std::size_t N>
    using getType = typename getTypeStruct<T, N>::type;

    template <int ...>
    struct iList;

    template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
    struct foo;

    template <std::size_t Top, std::size_t N, int ... Is>
    struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
    {
    using foo<Top, N+1u, iList<0, Is...>>::func;

    static void func (getType<K, Is> ... ts)
    { std::cout << sizeof...(ts) << std::endl; }
    };

    template <std::size_t Top, int ... Is>
    struct foo<Top, Top, iList<Is...>>
    {
    // fake func, for recursion ground case
    static void func ()
    { }
    };


    int main()
    {
    foo<>::func({1,'2',3.0}); // print 1
    foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
    foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
    }


    If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO



    #include <tuple>
    #include <iostream>
    #include <type_traits>

    using K = std::tuple<int, char, double>;

    template <std::size_t ... Is>
    constexpr auto getIndexSequence (std::index_sequence<Is...> is)
    -> decltype(is);

    template <std::size_t N>
    using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

    template <typename T, std::size_t>
    struct getTypeStruct
    { using type = T; };

    template <typename T, std::size_t N>
    using getType = typename getTypeStruct<T, N>::type;

    template <std::size_t N = 50, typename = IndSeqFrom<N>>
    struct foo;

    template <std::size_t N, std::size_t ... Is>
    struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
    {
    using foo<N-1u>::func;

    static void func (getType<K, Is> ... ts)
    { std::cout << sizeof...(ts) << std::endl; }
    };

    template <>
    struct foo<0, std::index_sequence<>>
    {
    static void func ()
    { std::cout << "0" << std::endl; }
    };

    int main()
    {
    foo<>::func({1,'2',3.0}); // print 1
    foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
    foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
    }


    It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance



    #include <tuple>
    #include <iostream>
    #include <type_traits>

    using K = std::tuple<int, char, double>;

    template <std::size_t ... Is>
    constexpr auto getIndexSequence (std::index_sequence<Is...> is)
    -> decltype(is);

    template <std::size_t N>
    using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

    template <typename T, std::size_t>
    struct getTypeStruct
    { using type = T; };

    template <typename T, std::size_t N>
    using getType = typename getTypeStruct<T, N>::type;

    template <std::size_t N, typename = IndSeqFrom<N>>
    struct bar;

    template <std::size_t N, std::size_t ... Is>
    struct bar<N, std::index_sequence<Is...>>
    {
    static void func (getType<K, Is> ... ts)
    { std::cout << sizeof...(ts) << std::endl; }
    };

    template <std::size_t N = 50, typename = IndSeqFrom<N>>
    struct foo;

    template <std::size_t N, std::size_t ... Is>
    struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
    { using bar<Is>::func...; };

    int main()
    {
    foo<>::func({1,'2',3.0}); // print 1
    foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
    foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
    }





    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53691855%2fproblem-with-calling-a-variadic-function-template-when-passing-brace-initialiser%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      8














      Generate an overloaded set of constructors:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;

      initializer(indexed<T, Is>... ts)
      {
      // ts is a pack of std::tuple<int, char, double>
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...> {};

      using foo = initializer<std::tuple<int, char, double>, 20>;
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO





      Generate an overloaded set of function call operators:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();

      int operator()(indexed<T, Is>... ts) const
      {
      // ts is a pack of std::tuple<int, char, double>
      return 1;
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...>
      {
      int operator()() const { return 0; }
      };

      static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 2





      Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:



      #include <array>
      #include <tuple>

      using K = std::tuple<int, char, double>;

      void foo(const std::array<K*, 5>& a)
      {
      // a is an array of at most 5 non-null std::tuple<int, char, double>*
      }

      void foo(K p0) { foo({&p0}); }
      void foo(K p0, K p1) { foo({&p0, &p1}); }
      void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
      void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
      void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 3





      Pass as an array and deduce its size (requires additional pair of parens):



      #include <tuple>
      #include <cstddef>

      template <std::size_t N>
      void foo(const std::tuple<int, char, double> (&a)[N])
      {
      // a is an array of exactly N std::tuple<int, char, double>
      }

      int main()
      {
      foo({{1,'2',3.0}, {4,'5',6.0}});
      // ^~~~~~ extra parens ~~~~~^
      }


      DEMO 4





      Use an std::initializer_list as a constructor parameter (to skip extra parens):



      #include <tuple>
      #include <initializer_list>

      struct foo
      {
      foo(std::initializer_list<std::tuple<int, char, double>> li)
      {
      // li is an initializer list of std::tuple<int, char, double>
      }
      };

      int main()
      {
      foo{ {1,'2',3.0}, {4,'5',6.0} };
      }


      DEMO 5






      share|improve this answer























      • intriguing the first one solution
        – max66
        Dec 9 at 14:25


















      8














      Generate an overloaded set of constructors:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;

      initializer(indexed<T, Is>... ts)
      {
      // ts is a pack of std::tuple<int, char, double>
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...> {};

      using foo = initializer<std::tuple<int, char, double>, 20>;
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO





      Generate an overloaded set of function call operators:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();

      int operator()(indexed<T, Is>... ts) const
      {
      // ts is a pack of std::tuple<int, char, double>
      return 1;
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...>
      {
      int operator()() const { return 0; }
      };

      static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 2





      Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:



      #include <array>
      #include <tuple>

      using K = std::tuple<int, char, double>;

      void foo(const std::array<K*, 5>& a)
      {
      // a is an array of at most 5 non-null std::tuple<int, char, double>*
      }

      void foo(K p0) { foo({&p0}); }
      void foo(K p0, K p1) { foo({&p0, &p1}); }
      void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
      void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
      void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 3





      Pass as an array and deduce its size (requires additional pair of parens):



      #include <tuple>
      #include <cstddef>

      template <std::size_t N>
      void foo(const std::tuple<int, char, double> (&a)[N])
      {
      // a is an array of exactly N std::tuple<int, char, double>
      }

      int main()
      {
      foo({{1,'2',3.0}, {4,'5',6.0}});
      // ^~~~~~ extra parens ~~~~~^
      }


      DEMO 4





      Use an std::initializer_list as a constructor parameter (to skip extra parens):



      #include <tuple>
      #include <initializer_list>

      struct foo
      {
      foo(std::initializer_list<std::tuple<int, char, double>> li)
      {
      // li is an initializer list of std::tuple<int, char, double>
      }
      };

      int main()
      {
      foo{ {1,'2',3.0}, {4,'5',6.0} };
      }


      DEMO 5






      share|improve this answer























      • intriguing the first one solution
        – max66
        Dec 9 at 14:25
















      8












      8








      8






      Generate an overloaded set of constructors:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;

      initializer(indexed<T, Is>... ts)
      {
      // ts is a pack of std::tuple<int, char, double>
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...> {};

      using foo = initializer<std::tuple<int, char, double>, 20>;
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO





      Generate an overloaded set of function call operators:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();

      int operator()(indexed<T, Is>... ts) const
      {
      // ts is a pack of std::tuple<int, char, double>
      return 1;
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...>
      {
      int operator()() const { return 0; }
      };

      static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 2





      Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:



      #include <array>
      #include <tuple>

      using K = std::tuple<int, char, double>;

      void foo(const std::array<K*, 5>& a)
      {
      // a is an array of at most 5 non-null std::tuple<int, char, double>*
      }

      void foo(K p0) { foo({&p0}); }
      void foo(K p0, K p1) { foo({&p0, &p1}); }
      void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
      void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
      void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 3





      Pass as an array and deduce its size (requires additional pair of parens):



      #include <tuple>
      #include <cstddef>

      template <std::size_t N>
      void foo(const std::tuple<int, char, double> (&a)[N])
      {
      // a is an array of exactly N std::tuple<int, char, double>
      }

      int main()
      {
      foo({{1,'2',3.0}, {4,'5',6.0}});
      // ^~~~~~ extra parens ~~~~~^
      }


      DEMO 4





      Use an std::initializer_list as a constructor parameter (to skip extra parens):



      #include <tuple>
      #include <initializer_list>

      struct foo
      {
      foo(std::initializer_list<std::tuple<int, char, double>> li)
      {
      // li is an initializer list of std::tuple<int, char, double>
      }
      };

      int main()
      {
      foo{ {1,'2',3.0}, {4,'5',6.0} };
      }


      DEMO 5






      share|improve this answer














      Generate an overloaded set of constructors:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;

      initializer(indexed<T, Is>... ts)
      {
      // ts is a pack of std::tuple<int, char, double>
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...> {};

      using foo = initializer<std::tuple<int, char, double>, 20>;
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO





      Generate an overloaded set of function call operators:



      #include <tuple>
      #include <cstddef>

      template <typename T, std::size_t M>
      using indexed = T;

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
      {
      using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();

      int operator()(indexed<T, Is>... ts) const
      {
      // ts is a pack of std::tuple<int, char, double>
      return 1;
      }
      };

      template <typename T, std::size_t M, std::size_t... Is>
      struct initializer<T, M, M, Is...>
      {
      int operator()() const { return 0; }
      };

      static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
      // tuples limit+1 ~~~^

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 2





      Create (or generate with preprocessor macros) a set of overloads that forward arguments to a single implementation:



      #include <array>
      #include <tuple>

      using K = std::tuple<int, char, double>;

      void foo(const std::array<K*, 5>& a)
      {
      // a is an array of at most 5 non-null std::tuple<int, char, double>*
      }

      void foo(K p0) { foo({&p0}); }
      void foo(K p0, K p1) { foo({&p0, &p1}); }
      void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
      void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
      void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }

      int main()
      {
      foo({1,'2',3.0});
      foo({1,'2',3.0}, {4,'5',6.0});
      foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
      }


      DEMO 3





      Pass as an array and deduce its size (requires additional pair of parens):



      #include <tuple>
      #include <cstddef>

      template <std::size_t N>
      void foo(const std::tuple<int, char, double> (&a)[N])
      {
      // a is an array of exactly N std::tuple<int, char, double>
      }

      int main()
      {
      foo({{1,'2',3.0}, {4,'5',6.0}});
      // ^~~~~~ extra parens ~~~~~^
      }


      DEMO 4





      Use an std::initializer_list as a constructor parameter (to skip extra parens):



      #include <tuple>
      #include <initializer_list>

      struct foo
      {
      foo(std::initializer_list<std::tuple<int, char, double>> li)
      {
      // li is an initializer list of std::tuple<int, char, double>
      }
      };

      int main()
      {
      foo{ {1,'2',3.0}, {4,'5',6.0} };
      }


      DEMO 5







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Dec 9 at 15:35

























      answered Dec 9 at 14:14









      Piotr Skotnicki

      34.5k471117




      34.5k471117












      • intriguing the first one solution
        – max66
        Dec 9 at 14:25




















      • intriguing the first one solution
        – max66
        Dec 9 at 14:25


















      intriguing the first one solution
      – max66
      Dec 9 at 14:25






      intriguing the first one solution
      – max66
      Dec 9 at 14:25















      4














      {} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:



      template <class T> struct A { T r; };
      template <class T>
      void foo (A<T> x);

      using K = A<int>;
      foo({1}); // fail
      foo(K{1}); // compile


      This is covered by [temp.deduc.call]/1




      If removing references and cv-qualifiers from P gives std::initializer_­list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




      and [temp.deduct.type]/5




      The non-deduced contexts are:



      (5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).




      When you:




      • explicitly provide template arguments, it works ... nothing to deduce

      • specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.






      share|improve this answer




























        4














        {} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:



        template <class T> struct A { T r; };
        template <class T>
        void foo (A<T> x);

        using K = A<int>;
        foo({1}); // fail
        foo(K{1}); // compile


        This is covered by [temp.deduc.call]/1




        If removing references and cv-qualifiers from P gives std::initializer_­list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




        and [temp.deduct.type]/5




        The non-deduced contexts are:



        (5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).




        When you:




        • explicitly provide template arguments, it works ... nothing to deduce

        • specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.






        share|improve this answer


























          4












          4








          4






          {} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:



          template <class T> struct A { T r; };
          template <class T>
          void foo (A<T> x);

          using K = A<int>;
          foo({1}); // fail
          foo(K{1}); // compile


          This is covered by [temp.deduc.call]/1




          If removing references and cv-qualifiers from P gives std::initializer_­list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




          and [temp.deduct.type]/5




          The non-deduced contexts are:



          (5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).




          When you:




          • explicitly provide template arguments, it works ... nothing to deduce

          • specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.






          share|improve this answer














          {} is not an expression hence don't have type, argument deduction is concerned about types, special care is taken when the argument used to perform argument deduction is an initializer list the template function parameter must have specifics forms, otherwise the parameter is a non-deduced context. A more simplistic example is this:



          template <class T> struct A { T r; };
          template <class T>
          void foo (A<T> x);

          using K = A<int>;
          foo({1}); // fail
          foo(K{1}); // compile


          This is covered by [temp.deduc.call]/1




          If removing references and cv-qualifiers from P gives std::initializer_­list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




          and [temp.deduct.type]/5




          The non-deduced contexts are:



          (5.6) A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).




          When you:




          • explicitly provide template arguments, it works ... nothing to deduce

          • specify the argument as K{1}, it works ... the argument is not longer an initializer list, is an expression with type.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 9 at 13:47

























          answered Dec 9 at 13:40









          Jans

          7,95022535




          7,95022535























              0















              I cannot use C++17 for this. A C++11-compatible solution is much preferred.




              With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as



              foo (std::tuple<int, char, double> ... ts)


              and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a



              func ();
              func (K t0);
              func (K t0, K t1);
              func (K t0, K t1, K t2);


              where K is your



              using K = std::tuple<int, char, double>;


              The following is a full compiling C++11 example



              #include <tuple>
              #include <iostream>

              using K = std::tuple<int, char, double>;

              template <typename T, std::size_t>
              struct getTypeStruct
              { using type = T; };

              template <typename T, std::size_t N>
              using getType = typename getTypeStruct<T, N>::type;

              template <int ...>
              struct iList;

              template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
              struct foo;

              template <std::size_t Top, std::size_t N, int ... Is>
              struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
              {
              using foo<Top, N+1u, iList<0, Is...>>::func;

              static void func (getType<K, Is> ... ts)
              { std::cout << sizeof...(ts) << std::endl; }
              };

              template <std::size_t Top, int ... Is>
              struct foo<Top, Top, iList<Is...>>
              {
              // fake func, for recursion ground case
              static void func ()
              { }
              };


              int main()
              {
              foo<>::func({1,'2',3.0}); // print 1
              foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
              foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
              }


              If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO



              #include <tuple>
              #include <iostream>
              #include <type_traits>

              using K = std::tuple<int, char, double>;

              template <std::size_t ... Is>
              constexpr auto getIndexSequence (std::index_sequence<Is...> is)
              -> decltype(is);

              template <std::size_t N>
              using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

              template <typename T, std::size_t>
              struct getTypeStruct
              { using type = T; };

              template <typename T, std::size_t N>
              using getType = typename getTypeStruct<T, N>::type;

              template <std::size_t N = 50, typename = IndSeqFrom<N>>
              struct foo;

              template <std::size_t N, std::size_t ... Is>
              struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
              {
              using foo<N-1u>::func;

              static void func (getType<K, Is> ... ts)
              { std::cout << sizeof...(ts) << std::endl; }
              };

              template <>
              struct foo<0, std::index_sequence<>>
              {
              static void func ()
              { std::cout << "0" << std::endl; }
              };

              int main()
              {
              foo<>::func({1,'2',3.0}); // print 1
              foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
              foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
              }


              It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance



              #include <tuple>
              #include <iostream>
              #include <type_traits>

              using K = std::tuple<int, char, double>;

              template <std::size_t ... Is>
              constexpr auto getIndexSequence (std::index_sequence<Is...> is)
              -> decltype(is);

              template <std::size_t N>
              using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

              template <typename T, std::size_t>
              struct getTypeStruct
              { using type = T; };

              template <typename T, std::size_t N>
              using getType = typename getTypeStruct<T, N>::type;

              template <std::size_t N, typename = IndSeqFrom<N>>
              struct bar;

              template <std::size_t N, std::size_t ... Is>
              struct bar<N, std::index_sequence<Is...>>
              {
              static void func (getType<K, Is> ... ts)
              { std::cout << sizeof...(ts) << std::endl; }
              };

              template <std::size_t N = 50, typename = IndSeqFrom<N>>
              struct foo;

              template <std::size_t N, std::size_t ... Is>
              struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
              { using bar<Is>::func...; };

              int main()
              {
              foo<>::func({1,'2',3.0}); // print 1
              foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
              foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
              }





              share|improve this answer




























                0















                I cannot use C++17 for this. A C++11-compatible solution is much preferred.




                With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as



                foo (std::tuple<int, char, double> ... ts)


                and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a



                func ();
                func (K t0);
                func (K t0, K t1);
                func (K t0, K t1, K t2);


                where K is your



                using K = std::tuple<int, char, double>;


                The following is a full compiling C++11 example



                #include <tuple>
                #include <iostream>

                using K = std::tuple<int, char, double>;

                template <typename T, std::size_t>
                struct getTypeStruct
                { using type = T; };

                template <typename T, std::size_t N>
                using getType = typename getTypeStruct<T, N>::type;

                template <int ...>
                struct iList;

                template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
                struct foo;

                template <std::size_t Top, std::size_t N, int ... Is>
                struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
                {
                using foo<Top, N+1u, iList<0, Is...>>::func;

                static void func (getType<K, Is> ... ts)
                { std::cout << sizeof...(ts) << std::endl; }
                };

                template <std::size_t Top, int ... Is>
                struct foo<Top, Top, iList<Is...>>
                {
                // fake func, for recursion ground case
                static void func ()
                { }
                };


                int main()
                {
                foo<>::func({1,'2',3.0}); // print 1
                foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                }


                If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO



                #include <tuple>
                #include <iostream>
                #include <type_traits>

                using K = std::tuple<int, char, double>;

                template <std::size_t ... Is>
                constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                -> decltype(is);

                template <std::size_t N>
                using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                template <typename T, std::size_t>
                struct getTypeStruct
                { using type = T; };

                template <typename T, std::size_t N>
                using getType = typename getTypeStruct<T, N>::type;

                template <std::size_t N = 50, typename = IndSeqFrom<N>>
                struct foo;

                template <std::size_t N, std::size_t ... Is>
                struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
                {
                using foo<N-1u>::func;

                static void func (getType<K, Is> ... ts)
                { std::cout << sizeof...(ts) << std::endl; }
                };

                template <>
                struct foo<0, std::index_sequence<>>
                {
                static void func ()
                { std::cout << "0" << std::endl; }
                };

                int main()
                {
                foo<>::func({1,'2',3.0}); // print 1
                foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                }


                It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance



                #include <tuple>
                #include <iostream>
                #include <type_traits>

                using K = std::tuple<int, char, double>;

                template <std::size_t ... Is>
                constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                -> decltype(is);

                template <std::size_t N>
                using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                template <typename T, std::size_t>
                struct getTypeStruct
                { using type = T; };

                template <typename T, std::size_t N>
                using getType = typename getTypeStruct<T, N>::type;

                template <std::size_t N, typename = IndSeqFrom<N>>
                struct bar;

                template <std::size_t N, std::size_t ... Is>
                struct bar<N, std::index_sequence<Is...>>
                {
                static void func (getType<K, Is> ... ts)
                { std::cout << sizeof...(ts) << std::endl; }
                };

                template <std::size_t N = 50, typename = IndSeqFrom<N>>
                struct foo;

                template <std::size_t N, std::size_t ... Is>
                struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
                { using bar<Is>::func...; };

                int main()
                {
                foo<>::func({1,'2',3.0}); // print 1
                foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                }





                share|improve this answer


























                  0












                  0








                  0







                  I cannot use C++17 for this. A C++11-compatible solution is much preferred.




                  With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as



                  foo (std::tuple<int, char, double> ... ts)


                  and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a



                  func ();
                  func (K t0);
                  func (K t0, K t1);
                  func (K t0, K t1, K t2);


                  where K is your



                  using K = std::tuple<int, char, double>;


                  The following is a full compiling C++11 example



                  #include <tuple>
                  #include <iostream>

                  using K = std::tuple<int, char, double>;

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <int ...>
                  struct iList;

                  template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
                  struct foo;

                  template <std::size_t Top, std::size_t N, int ... Is>
                  struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
                  {
                  using foo<Top, N+1u, iList<0, Is...>>::func;

                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <std::size_t Top, int ... Is>
                  struct foo<Top, Top, iList<Is...>>
                  {
                  // fake func, for recursion ground case
                  static void func ()
                  { }
                  };


                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }


                  If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO



                  #include <tuple>
                  #include <iostream>
                  #include <type_traits>

                  using K = std::tuple<int, char, double>;

                  template <std::size_t ... Is>
                  constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                  -> decltype(is);

                  template <std::size_t N>
                  using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <std::size_t N = 50, typename = IndSeqFrom<N>>
                  struct foo;

                  template <std::size_t N, std::size_t ... Is>
                  struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
                  {
                  using foo<N-1u>::func;

                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <>
                  struct foo<0, std::index_sequence<>>
                  {
                  static void func ()
                  { std::cout << "0" << std::endl; }
                  };

                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }


                  It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance



                  #include <tuple>
                  #include <iostream>
                  #include <type_traits>

                  using K = std::tuple<int, char, double>;

                  template <std::size_t ... Is>
                  constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                  -> decltype(is);

                  template <std::size_t N>
                  using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <std::size_t N, typename = IndSeqFrom<N>>
                  struct bar;

                  template <std::size_t N, std::size_t ... Is>
                  struct bar<N, std::index_sequence<Is...>>
                  {
                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <std::size_t N = 50, typename = IndSeqFrom<N>>
                  struct foo;

                  template <std::size_t N, std::size_t ... Is>
                  struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
                  { using bar<Is>::func...; };

                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }





                  share|improve this answer















                  I cannot use C++17 for this. A C++11-compatible solution is much preferred.




                  With C++11 is a little more complicated (no std::index_sequence, no std::make_index_sequence) but, if you want maintain the variadic use of tuples... that is... if you substantially want something as



                  foo (std::tuple<int, char, double> ... ts)


                  and if you accept to call a static method of a template struct, you can define a template struct that recursively inherit itself and, recursively, define a



                  func ();
                  func (K t0);
                  func (K t0, K t1);
                  func (K t0, K t1, K t2);


                  where K is your



                  using K = std::tuple<int, char, double>;


                  The following is a full compiling C++11 example



                  #include <tuple>
                  #include <iostream>

                  using K = std::tuple<int, char, double>;

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <int ...>
                  struct iList;

                  template <std::size_t = 50u, std::size_t = 0u, typename = iList<>>
                  struct foo;

                  template <std::size_t Top, std::size_t N, int ... Is>
                  struct foo<Top, N, iList<Is...>> : public foo<Top, N+1u, iList<0, Is...>>
                  {
                  using foo<Top, N+1u, iList<0, Is...>>::func;

                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <std::size_t Top, int ... Is>
                  struct foo<Top, Top, iList<Is...>>
                  {
                  // fake func, for recursion ground case
                  static void func ()
                  { }
                  };


                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }


                  If you can use C++14, you can use std::make_index_sequence and std::index_sequence and the code become a little better, IMHO



                  #include <tuple>
                  #include <iostream>
                  #include <type_traits>

                  using K = std::tuple<int, char, double>;

                  template <std::size_t ... Is>
                  constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                  -> decltype(is);

                  template <std::size_t N>
                  using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <std::size_t N = 50, typename = IndSeqFrom<N>>
                  struct foo;

                  template <std::size_t N, std::size_t ... Is>
                  struct foo<N, std::index_sequence<Is...>> : public foo<N-1u>
                  {
                  using foo<N-1u>::func;

                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <>
                  struct foo<0, std::index_sequence<>>
                  {
                  static void func ()
                  { std::cout << "0" << std::endl; }
                  };

                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }


                  It's a pity you can't use C++17 because in you could use variadic unsing and avoid at all recursive inheritance



                  #include <tuple>
                  #include <iostream>
                  #include <type_traits>

                  using K = std::tuple<int, char, double>;

                  template <std::size_t ... Is>
                  constexpr auto getIndexSequence (std::index_sequence<Is...> is)
                  -> decltype(is);

                  template <std::size_t N>
                  using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));

                  template <typename T, std::size_t>
                  struct getTypeStruct
                  { using type = T; };

                  template <typename T, std::size_t N>
                  using getType = typename getTypeStruct<T, N>::type;

                  template <std::size_t N, typename = IndSeqFrom<N>>
                  struct bar;

                  template <std::size_t N, std::size_t ... Is>
                  struct bar<N, std::index_sequence<Is...>>
                  {
                  static void func (getType<K, Is> ... ts)
                  { std::cout << sizeof...(ts) << std::endl; }
                  };

                  template <std::size_t N = 50, typename = IndSeqFrom<N>>
                  struct foo;

                  template <std::size_t N, std::size_t ... Is>
                  struct foo<N, std::index_sequence<Is...>> : public bar<Is>...
                  { using bar<Is>::func...; };

                  int main()
                  {
                  foo<>::func({1,'2',3.0}); // print 1
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}); // print 2
                  foo<>::func({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0}); // print 3
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Dec 9 at 16:59

























                  answered Dec 9 at 16:39









                  max66

                  34.2k63762




                  34.2k63762






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53691855%2fproblem-with-calling-a-variadic-function-template-when-passing-brace-initialiser%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

                      ComboBox Display Member on multiple fields

                      Is it possible to collect Nectar points via Trainline?