3

Can I default initialize a parameter pack to the respective value initialization of each type ?

To elaborate a bit more, take the example of a simple function template

template<typename T>
void f(T arg = T())
{ 
  // eg for T=int, arg is 0 (value initialization) when default initialized
}

Would it be possible to express its variadic counterpart, ie

template<typename... Args>
void F(Args... args /* how can I value initialize the parameter pack? */)
{
}
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63

3 Answers3

3

You can create two parameter packs, one representing the types corresponding to function parameters and one representing "defaulted parameters."

template< typename ... aux, typename ... arg >
void fn( arg ... a ) {
    std::tuple< aux ... > more {}; // The tuple elements are value-initialized.
}

http://coliru.stacked-crooked.com/a/1baac4b877dce4eb

There is no way to explicitly mention the deduced template parameters for this function. Anything inside the angle braces of the call will go into aux, not arg.

Note, the initialization you get with {} is value-initialization, not default-initialization. Objects of fundamental type get zeroed, not left uninitialized.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Ha I was just (simultaneously) mentioning the tuple hack in the question's comments. Cool double variadic template args :) +1 – Lorah Attkins Dec 29 '14 at 10:23
  • @NorahAttkins Another variation would be `std::tuple< arg ..., aux ... > more { std::move( a ) ..., aux {} ... };`. This would effectively merge the two packs back in the order you want them. – Potatoswatter Dec 29 '14 at 10:32
  • @NorahAttkins hold on, where do the explicitly given type template arguments go: are all of them auxiliary paramaters to be value-initialized, or only those that are not given as function call arguments? does `F(1)` have one argument `1`, or two arguments: `1` and value-initialized `0` ? – Piotr Skotnicki Dec 29 '14 at 10:35
  • @PiotrS. I think that the original problem description would give it one argument, as in your solution, but I've presented a simpler solution where it represents two arguments. Your solution appears to be an extension of mine to satisfy that particular requirement, where you manually skip over initial explicit arguments. – Potatoswatter Dec 29 '14 at 10:43
3
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>

template <typename... Args>
void F(Args... args)
{
    // target function, arbitrary body
    using expander = int[];
    (void)expander{ 0, (void(std::cout << args << " "), 0)... };
    std::cout << std::endl;
}

template <typename... Args, typename... Params, std::size_t... Is>
void F(std::index_sequence<Is...>, Params&&... params)
{
    F<Args...>(std::forward<Params>(params)...
             , std::decay_t<typename std::tuple_element<sizeof...(Params) + Is, std::tuple<Args...>>::type>{}...);
}

template <typename... Args, typename... Params>
auto F(Params&&... params)
    -> std::enable_if_t<(sizeof...(Args) > sizeof...(Params))>
{
    F<Args...>(std::make_index_sequence<sizeof...(Args) - sizeof...(Params)>{}
             , std::forward<Params>(params)...);
}

Tests:

#include <string>

int main()
{
    // F(int, char, float = float{}, double = double{})
    F<int, char, float, double>(1, 'c');

    // F(int = int{}, char = char{}, float = float{}, double = double{})     
    F<int, char, float, double>();

    // F(const std::string&, const std::string& = std::string{})
    F<const std::string&, const std::string&>("foo");

    // F(int, int, int)
    F(1, 2, 3);
}

Output:

1 'c' 0 0 
0 '\0' 0 0
"foo" ""
1 2 3

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • OK, you had my +1 from the beginning. But to accept the answer I have to understand it a bit more. I'll mention what I understand, correct any of the things I'm wrong about : (1) What you propose is to wrap the function in a calling system that will provide default (value initialized) args for the non provided ones : `F` is the wrapping system and `F3` is the end func (2) what's the expander ? is it a coumpound statement? (3) `Params` - `Args` will be the number of args filled in and `Params` can overlap with `Args` – Lorah Attkins Dec 29 '14 at 11:01
  • @NorahAttkins `F3` is a target function, I just put there `expander` which simply prints out all arguments, but its body can be customized to your needs. `Params-Args` gives you the number of explicit type template parameter for which the argument was not provided. Eventually, those missing arguments are value-initialized and passed in to `F3` – Piotr Skotnicki Dec 29 '14 at 11:05
  • Just noticed the `...` in the expander. Am I that a newbie to see this use as wowing (don't answer that!). It would be great if you could provide any link for this hack, language feature, use ... anything. Thnx for the answer, I learned a lot! – Lorah Attkins Dec 29 '14 at 11:12
  • @NorahAttkins see [this explanation](http://stackoverflow.com/a/25683817/3953764) – Piotr Skotnicki Dec 29 '14 at 13:08
  • thnx for the link. I now get it; even gave a [try](http://stackoverflow.com/a/27692793/4224575) at this – Lorah Attkins Dec 29 '14 at 16:54
2

It`s explicitly forbidden by C++ standard, you cannot do such thing. N3376 8.3.6/3

A default argument shall be specified only in the parameter-declaration-clause of a function declaration or in a template-parameter (14.1); in the latter case, the initializer-clause shall be an assignment-expression. A default argument shall not be specified for a parameter pack.

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • @NorahAttkins you can write two overloads, one with arguments and one without, first will use default parameters and second specified. – ForEveR Dec 29 '14 at 10:02