A function that accepts a variadic list of object patterns built on curly braces

Hello template meta-programming specialists.

I am trying to write a function (a member of a class) that can ideally take as an argument what might be called an agnostic typemap.

Ideally something like:

foo({"Bar", 42}, {"Baz", "Blargh");



foo({"Blargh", "Bleh"}, {"Biz", 43.6}, {"Bam", {43, 43, 43}});


The class / function can then convert those arguments by calling the conversion function inferred at compile time, so it will internally end up with std :: map with strings as keys and values.

The reason is just syntactic sugar. In my case, it would be very convenient for the caller not to worry about argument conversion and getting a compile-time error if an unsupported type was supplied.

I did this if the caller was explicitly using make_pair, as in:

foo(std::make_pair<std::string, std::chrono::miliseconds>("Time", 42));


But naturally, this is not very clean and definitely not more convenient than when calling to convert value types to std :: string.

I've tried creating my own std :: pair class as a specialization for different value types, but my compiler (gcc) can't find it if I call with a parenthesis initializer, as is the case with the standard std :: map. My compiler seems to see it as std :: initializer_list even though the arguments are of different types.

I've more or less come to the conclusion that what I'm trying is not possible with C ++ even in the C ++ 14 standard, but I'm not entirely sure.

Does anyone have any ideas on how to solve this or can they explain why this is not possible if it is?

Thank you so much!


Sample code:

template<typename Value, typename... Args>
void foo(const std::pair<std::string, Value>& val, Args... args)

void foo(const std::pair<std::string, int>& val) {}

void foo(){}


Call foo () like this:

foo({"key", 42});


does not extend the template and works, but:

foo({"key", 42}, {"another", 53})


fails to compile with error:

no matching function for call to ‘foo(<brace-enclosed initializer list>, <brace-enclosed initializer list>)’



source to share

2 answers

I assume the following code:

template <typename... Ts>
void foo(Ts... ts) {}

foo({1,2}, {3,4});


fails to compile for the same reason as:

template <typename T>
void foo(T t) {}



which is that the list enclosed in curly braces has no type at all and therefore cannot be deduced.

However, knowing that an initializer list enclosed in curly braces can be used to initialize a particular type with an implicit constructor, and that compiler can infer the type T


, the following code, using the old-fashioned variadic list, works as intended:

#include <initializer_list>

struct AgnosticMap
    AgnosticMap(std::nullptr_t) {}

    template <typename T, typename U>
    AgnosticMap(T t, U u) {}

    template <typename T, typename U>
    AgnosticMap(T t, std::initializer_list<U> il) {}

void foo(AgnosticMap a1
       , AgnosticMap a2 = nullptr
       , AgnosticMap a3 = nullptr
       , AgnosticMap a4 = nullptr)

int main()
    foo({"Blargh", "Bleh"}, {"Biz", 43.6}, {"Bam", {43, 43, 43}});





One solution I can imagine is using variadic templates:

template<typename T, typename U, typename... Rest>
auto foo(T t, U u, Rest... rest){
    //deal with t and u,
    //and call foo(rest...) recursievely




All Articles