Variable list of pairs as function argument [duplicate] - c++

Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }

If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));

You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}

Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.

Related

How to use second overload of std::optional<T>::emplace

In the std::optional::emplace docs there is an overload that accepts std::initializer_list:
template< class U, class... Args >
T& emplace( std::initializer_list<U> ilist, Args&&... args );
provided that
std::is_constructible<T, std::initializer_list&, Args&&...>::value is true
I thought that it might be used to emplace POD types, but apparently this is not how it works (in other SO topics it was explained that emplace functions are using () syntax instead of {}):
struct A
{
int x;
int y;
int z;
};
int main()
{
A normalA{1, 2, 3}; // this is OK
std::cout << std::is_constructible<A, std::initializer_list<int>&, int, int, int>::value << std::endl; // false
std::cout << std::is_constructible<A, std::initializer_list<int>&>::value << std::endl; // false
std::optional<A> optA;
// optA.emplace({1, 2, 3}); // this is NOK
optA.emplace(A{1, 2, 3}); // I can walk it around with copy-ctor
}
I can write the constructor accepting initializer_list:
struct B
{
B(std::initializer_list<int> l) {/* some impl here */}
int x;
int y;
int z;
};
and then call emplace like this:
std::optional<B> optB;
optB.emplace({1, 2, 3});
but shouldn't first emplace overload T& emplace( Args&&... args ); be enough for that?
I thought that it might be useful for the array types, but std::optional<int[]> xxx; does not compile anyway.
Can you please provide some example where second std::optional::emplace overload is used.
but shouldn't first emplace overload T& emplace( Args&&... args ); be enough for that?
It isn't because a braced-init-list, i.e. {1, 2, 3} has no type. Because it has no type, there is nothing to compiler can do to deduce what Args should be. We need to have an overload that explicitly takes a std::initializer_list so that we can avoid the compiler not being able to deduce what the braced-init-list should be considered as.

Ensuring template argument type matches that of its variadic constructor

I would like to have a class like this:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if<std::is_same<T,Ts>...>...>
explicit Foo(Ts...ts) : data_{ ts... } {}
};
However; something with the syntax is wrong, and I'm not sure if you can set parameters into a pointer directly like this upon initialization.
What I would like for this to do is simply this:
Foo<int> f1{ 1, 3, 5, 7 }; // Or
// Foo<int> f1( 1, 3, 5 7 );
// f1.data_[0] = 1
// f1.data_[1] = 3
// f1.data_[2] = 5
// f1.data_[3] = 7
// f1.data_[4] = ... not our memory either garbage or undefined...
Foo<float> f2{ 3.5f, 7.2f, 9.8f }; // Or
// Foo<float> f2( 3.5f, 7.2f, 9.8f );
// f2.data_[0] = 3.5
// f2.data_[1] = 7.2
// f2.data_[2] = 9.8
// f2.data_[3] = ... not our memory
I would also like to have the constructor check to make sure that each and every parameter that is passed into the constructor is of type <T>; simply put for each Ts it must be a T.
I might be overthinking this but for the life of me I can not get this or something similar to compile. I don't know if it's within enable_if, is_same or through the class's initializer list and trying to store the contents into a pointer. I don't know if I should use an array of T instead but the array's size won't be known until the arguments are passed into the constructor. I'm also trying to do this without using a basic container such as std::vector; it's more for self education than practical source code. I just want to see how this could be done with raw pointers.
Edit
I've changed my class to something like this:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if_t<std::is_same<T, Ts...>::value>* = nullptr>
explicit Foo( const Ts&&... ts ) : data_{ std::move(ts)... } {}
};
And when trying to use it:
int a = 1, b = 3, c = 5, d = 7;
Foo<int> f1( a, b, c, d );
Foo<int> f2{ a, b, c, d };
I'm a little closer with this iteration; but they both give different compiler errors.
The first being: C2661: "No overloaded function takes 4 arguments"
And the second: C2440: "initializing, cannot convert from initializer list to Container, no constructor could take the source type, or constructor overload resolution was ambiguous."
Why not simply use a std::initialize_list:?
#include <iostream>
#include <type_traits>
#include <vector>
template <class T>
struct Foo
{
std::vector<T> data_;
explicit Foo(std::initializer_list<T> data) : data_(data)
{
std::cout << "1";
};
template <typename... Ts,
typename ENABLE=std::enable_if_t<(std::is_same_v<T,Ts> && ...)> >
explicit Foo(Ts... ts) : Foo(std::initializer_list<T>{ts...})
{
std::cout << "2";
}
};
int main()
{
Foo<int> f1{1, 3, 5, 7}; // prints 1
Foo<int> f2(1, 3, 5, 7); // prints 1 then 2
return 0;
}
If some Ts are different from T you will get a compile-time error.
With
gcc -std=c++17 prog.cpp
you get:
Foo<int> f1{1, 3, 5., 7};
error: narrowing conversion of ‘5.0e+0’ from ‘double’ to ‘int’ inside
{ } [-Wnarrowing] Foo f1{1, 3, 5., 7};
^
and
Foo<int> f2(1, 3, 5., 7);
you get
error: no matching function for call to ‘Foo::Foo(int, int,
double, int)’ Foo f2(1, 3, 5., 7);
^ note: candidate: ‘template Foo::Foo(Ts ...)’ explicit Foo(Ts... ts) :
Foo(std::initializer_list{ts...})
...
Update: if you really want to use something like raw pointer, here is a complete working example:
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
template <class T>
struct Foo
{
size_t n_;
std::unique_ptr<T[]> data_;
explicit Foo(std::initializer_list<T> data) : n_(data.size()), data_(new T[n_])
{
std::copy(data.begin(), data.end(), data_.get());
std::cout << "1";
};
template <typename... Ts, typename ENABLE = std::enable_if_t<(std::is_same_v<T, Ts> && ...)> >
explicit Foo(Ts... ts) : Foo(std::initializer_list<T>{ts...})
{
std::cout << "2";
}
friend std::ostream& operator<<(std::ostream& out, const Foo<T>& toPrint)
{
for (size_t i = 0; i < toPrint.n_; i++)
std::cout << "\n" << toPrint.data_[i];
return out;
}
};
int main()
{
Foo<int> f1{1, 3, 5, 7}; // prints 1
Foo<int> f2(1, 3, 5, 7); // prints 1,2
std::cout << f1;
std::cout << f2;
return 0;
}
I let you replace unique_ptr by a raw pointer with all the extra work: delete[] etc...
std::is_same only compares two types, and you can't use pack expansions to declare multiple template parameters. That means you'll need to pull all of your std::is_same checks out into another check:
template <typename T, typename... Ts>
struct all_same : std::bool_constant<(std::is_same<T, Ts>::value && ...)> {};
template <typename T>
struct Foo
{
std::vector<T> data_;
template <typename... Ts, std::enable_if_t<all_same<T, std::decay_t<Ts>...>::value>* = nullptr>
Foo(Ts&&... ts)
: data_{std::forward<Ts>(ts)...}
{
}
};
Live Demo
You also need to allocate memory for your data_ array. Here I've used std::vector to take care of that allocation for me, but you could use new[] and delete[] to manage it yourself if you really want to.
enable_if and is_same won't store anything anywhere, they are only compile-time constructs and do not yield to any code in the binary executable.
Regardless of the syntax, what your code is essentially doing is trying to take the address of a constructor argument (which is a temporary). This will be a dangling pointer as soon as the constructor exits.
Either Foo owns the memory area and must allocate in constructor and delete in destructor (if any doubt: use std::vector!), or it aliases some external memory, and must receive a pointer to that memory.
Now regarding syntax:
std::is_same is a template that provides a value boolean constant and is to be used like so: std::is_same<T1, T2>::value. Alternatively you can use std::is_same_v<T1, T2>.
std::enable_if provides a type type member, only if the constant expression (1st template parameter) is true. Use it like std::enable_if<expr, T>::type. If expr is true, type is a typedef to T. Otherwise it is not defined and yields a substitution failure. Alternatively you can use std::enable_if_t<expr, T>
You can have a look here for a similar approach of yours.
But you can also simplify all this by using a member vector. In that case, the vector constructor ensures that all arguments have compatible types. Here is a complete example:
#include <string>
#include <vector>
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
struct Foo {
vector<T> data_;
template<typename ...Ts>
explicit Foo(Ts... ts) : data_{ ts... } {}
void print() {
for (const auto &v : data_) {
cout << v << " ";
}
cout << endl;
}
};
int main() {
Foo<int> ints { 1, 2, 3, 4, 5 };
Foo<string> strings { "a", "b", "c", "d", "e"};
// Foo<string> incorrect { "a", 2, "c", 4, "e"};
ints.print();
strings.print();
// incorrect.print();
return 0;
}
The most idiomatic way to do this in C++17 is using std::cunjunction_v. It lazily evaluates subsequent values and allows you to avoid fold expressions. Also messages generated by the compiler are the same for both of casese you mentioned in edited piece of code.
Also, passing data by const-rvalue-ref makes no sense, since it is impossible to move data from const objects. Additionaly you were moving pack of arguments to a pointer. I didn't know what to do about it so just removed it.
Additionaly, take a look at the std::decay_t - without it it would not work, as sometimes Ts is deduced not as int but as const int & or int &. This leads to std::is_same_v being false.
The code below compiles fine at godbolt:
template<typename T>
struct Foo {
T* data_;
template<
typename... Ts,
std::enable_if_t<
std::conjunction_v<
std::is_same<T, std::decay_t<Ts>>...
>
> * = nullptr
>
explicit Foo( Ts&&... ts ) : data_{ } {}
};

How to pass variadic amount of `std::pair` with different 2nd types to a function

Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }
If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));
You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}
Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.

Overload function template based on parameter function argument type

I'm a class that looks like this:
template<typename A>
struct List {
...
template<typename Fn, typename B = typename std::result_of<Fn(A)>::type>
List<B> map(Fn f) const
{ ... }
};
I'm trying to overload map and allow it to accept a getter of type A as argument, so that we can do foos.map(&Foo::bar), where bar is a getter of class Foo. The following function works:
template<typename Fn, typename B = typename std::result_of<Fn(A*)>::type>
List<B> mapGet(Fn getter) const
{ ... }
But if I try to use the same name map, the compiler complains it's ambiguous. My question is, when Fn is a getter wouldn't the former std::result_of fail, effectively disabling one of the overloaded maps? Also, is there any way to make overloading possible?
My question is, when Fn is a getter wouldn't the former std::result_of fail, effectively disabling one of the overloaded maps?
I'm guessing by "getter" what you really mean is pointer to member function? In which case, std::result_of works just fine with those. Let's say we have some type Foo:
struct Foo {
Foo(int i) : i(i) { }
int bar() const { return i; }
int i;
};
You can use pointers to members as you'd expect:
using T = std::result_of_t<decltype(&Foo::bar)(Foo )>;
static_assert(std::is_same<T, int>{}, "!");
The only difference is how you actually call f. For C++17, there's std::invoke() which will work with all the invokable types, otherwise you could just directly use std::bind() or write your own wrapper which does the same thing.
As an example and ignoring copying, forwarding, and reserving, we could write map like:
template <class A, class F, class B = std::result_of_t<F(A)>>
std::vector<B> map(std::vector<A> xs, F f)
{
auto binder = std::bind(f, std::placeholders::_1);
std::vector<B> r;
for (auto& x : xs) {
r.push_back(binder(x));
}
return r;
}
That's works just as well for actual function objects:
std::vector<int> vs{1, 2, 3, 4, 5};
std::vector<double> ds = map(vs, [](int i){return i * 2.0; });
As it does for our Foo with a pointer to member:
std::vector<Foo> foos{1, 2, 3, 4, 5};
std::vector<int> is = map(foos, &Foo::bar);

Combine Template for Perfect Forwarding and Tempate for arbitrary value_type

I have this function template foo that takes any STL container that contains int:
template <typename ContainerType, std::enable_if_t<std::is_same<typename ContainerType::value_type, int>::value, int> = 0>
void foo(ContainerType const& list)
{ /* */ }
And I have this function template bar that takes a std::vector<int> and transform it for perfect forwarding (or reference forwarding, whatever you may call it):
template <typename ContainerType, std::enable_if_t<std::is_same<std::decay_t<ContainerType>, std::vector<int>>::value, int> = 0>
void bar(ContainerType&& list)
{ /* */ }
int main(void)
{
std::initializer_list<int> list{1, 2, 3};
std::vector<int> vec{1, 2, 3};
foo(list); // OK
foo(vec); // OK
foo(std::vector<int>{4, 5, 6}); // OK, but copy-semantics
bar(vec); // OK
bar(std::vector<int>{4,5,6}); // OK
bar(list); // ERROR
}
I want to combine these two into one, to get a template function that accepts STL Containers with value_type int and prepare them for perfect forwarding. How I can accomplish that?
Just combine your conditions. We want a function that takes a forwarding-reference:
template <class C, class = std::enable_if_t<???> >
void quux(C&& container);
And you want that the value_type of the underlying container is int. Let's throw that into its own trait for readability:
template <class C>
using is_int_container = std::is_same<typename C::value_type, int>;
Now, can't just do is_int_container<C> because C at the moment can be a reference or reference to const. But we can just fix that with std::decay:
template <class C, class = std::enable_if_t<is_int_container<std::decay_t<C>>::value >>
void quux(C&& container);