Differentiate functions when overloading - c++

I'm writing two functions with the same name (and with similar parameters):
The first one only takes a variable amount of integer parameters (minimum one).
The second one takes a variable amount of struct coordinate as parameters (again minimum one).
The struct coordinate can be constructed from both int and std::vector<int>, so the second one can in that way also take int and std::vector<int>.
The problem is that if the first parameter to the second function is an int then the compilation fails.
Is it possible to make the third line in main() call the other tmp function?
My current implementation is listed below.
int main(int argc, char** argv)
{
tmp(1, 2, 3, 4, 5); // OK! First function calls 'single'
tmp(std::vector<int>{1, 2, 3}, 4, 5); // OK! Second function calls 'multi'
tmp(1, std::vector<int>{2, 3}, 4); // Compilation error! First function calls 'single'
return 0;
}
Compiler output:
prog.cpp: In instantiation of ‘std::vector<int> tmp(int, types ...) [with types = {std::vector<int, std::allocator<int> >, int}]’:
prog.cpp:58:34: required from here
prog.cpp:29:23: error: no matching function for call to ‘single(std::vector<int>&, std::vector<int>&, int&)’
single(list, ints ...);
^
prog.cpp:29:23: note: candidates are:
prog.cpp:12:6: note: void single(std::vector<int>&, int)
void single(std::vector<int>& list, int first)
^
prog.cpp:12:6: note: candidate expects 2 arguments, 3 provided
prog.cpp:18:6: note: template<class ... types> void single(std::vector<int>&, int, types ...)
void single(std::vector<int>& list, int first, types ... ints)
^
prog.cpp:18:6: note: template argument deduction/substitution failed:
prog.cpp:29:23: note: cannot convert ‘ints#0’ (type ‘std::vector<int>’) to type ‘int’
single(list, ints ...);
^
My current implementation:
#include <iostream>
#include <vector>
struct coordinate
{
std::vector<int> c;
coordinate(std::vector<int> A) : c(A) {}
coordinate(int A) : c{A} {}
};
// Function to end the recursive call to single
void single(std::vector<int>& list, int first)
{
list.push_back(first);
}
// Recursive function to store the parameters (only int)
template <typename... types>
void single(std::vector<int>& list, int first, types ... ints)
{
list.push_back(first);
single(list, ints ...);
}
// 'First' function
template <typename... types>
std::vector<int> tmp(int i, types ... ints)
{
std::vector<int> list;
list.push_back(i);
single(list, ints ...);
return list;
}
// Function to end the recursive call to multi
void multi(std::vector<std::vector<int> >& list, coordinate first)
{
list.push_back(first.c);
}
// Recursive function for storing the parameters (only 'coordinate')
template <typename... types>
void multi(std::vector<std::vector<int> >& list, coordinate first, types ... coords)
{
list.push_back(first.c);
multi(list, coords ...);
}
// 'Second' function
template <typename... types>
std::vector<std::vector<int> > tmp(coordinate i, types ... coords)
{
std::vector<std::vector<int> > list;
list.push_back(i.c);
multi(list, coords ...);
return list;
}

Alright, based on my new understanding from the comment you want something like this:
template <typename... Args>
typename std::enable_if<all_ints<Args...>::value>::type
tmp(Args... args) {
// the all integer version
}
template <typename... Args>
typename std::enable_if<!all_ints<Args...>::value>::type
tmp(Args... args) {
// the coordinate version
}
Then you just need to write the type trait to check if everything is an integer.
template <typename... T>
struct all_ints : std::true_type { };
template <typename T, typename... Rest>
struct all_ints<T, Rest...>
: std::integral_constant<bool,
std::is_integral<T>::value && all_ints<Rest...>::value>
{ }
I used std::is_integal to handle all the integer types. If you really want explicitly int, you can fix it.

Related

Why does my variadic template instantiation not work?

I am revisiting C++ after a long hiatus, and I would like to use templates to design the known "map" function -- the one which applies a function to every element of a collection.
Disregarding the fact my map doesn't return anything (a non-factor here), I have managed to implement what I wanted if the function passed to "map" does not need to accept additional arguments:
#include <iostream>
template <typename C, void fn(const typename C::value_type &)> void map(const C & c) {
for(auto i : c) {
fn(i);
}
}
struct some_container_type { /// Just some hastily put together iterable structure type
typedef int value_type;
value_type * a;
int n;
some_container_type(value_type * a, int n): a(a), n(n) { }
value_type * begin() const {
return a;
}
value_type * end() const {
return a + n;
}
};
void some_fn(const int & e) { /// A function used for testing the "map" function
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, some_fn>(sc);
}
However, I would like map to accept additional arguments to call fn with. I've tried to compile the modified variant of the program (container type definition was unchanged):
template <typename C, typename ... T, void fn(const typename C::value_type &, T ...)> void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
void some_fn(const int & e, int a, float b, char c) {
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, int, float, char, some_fn>(sc, 1, 2.0f, '3');
}
But gcc -std=c++20 refuses to compile the modified program containing the above variant, aborting with:
<source>: In function 'int main()':
<source>:29:56: error: no matching function for call to 'map<some_container_type, int, float, char, some_fn>(const some_container_type&, int, int, int)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:16:97: note: candidate: 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
16 | template <typename C, typename ... T, void fn(const typename C::value_type &, T ... args)> void map(const C & c, T ... args) {
| ^~~
<source>:16:97: note: template argument deduction/substitution failed:
<source>:29:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:29:56: note: expected a type, got 'some_fn'
Microsoft Visual C++ compiler (19.24.28314) gives a more descriptive error message:
error C3547: template parameter 'fn' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'map'
Can someone explain if and how I can idiomatically accomplish for map to accept arbitrary arguments for forwarding these to fn?
I know I can pass fn to the map function as argument instead of specifying it as an argument to the template, but for reasons related to inlining and to better understand C++ templates, I'd like to retain fn a template rather than a function parameter.
I also don't want to use any libraries, including the standard library (what use of std I show in the examples above is only for clarifying the question). I know there are "functor" and "forward" somewhere in the libraries, but I suppose they too were written in C++, so I am curious if my problem can be solved without any libraries.
A simple way to fix this would be to deduce the non-type template parameter for the function, and reorder the template parameter list
template <typename C, auto fn, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
and then call it like this
map<some_container_type, some_fn, int, float, char>(sc, 1, 2.0f, '3');
Here's a demo
You could also move fn to the beginning of the template parameter list.
template <auto fn, typename C, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
Now since C and T can be deduced from the function arguments, this makes the call site much cleaner
map<some_fn>(sc, 1, 2.0f, '3');
Here's a demo

Accept template as function parameter

Could someone explain the difference between the two function calls here?
When can you pass a templated variable to a function?
In both cases I should end up with an templated array inside the function, but only one compiles.
template<int DIM>
struct MyStruct
{
array<int, DIM> structArr;
};
template<int DIM> void testA( MyStruct <DIM>& myStruct) { }
template<int DIM> void testB( array<int, DIM>& arrA) { }
int main()
{
MyStruct<3> myStruct;
array<int, 3> arr;
testA(myStruct);
testB(arr); //compile error
return 0;
}
EDIT:
Error messages look like this:
error: no matching function for call to ‘testB(std::array&)’
testB(arr); //compile error
^
note: candidate: template void testB(std::array&)
template<int DIM> void testB( array<int, DIM>& arrA) { }
^~~~~
note: template argument deduction/substitution failed:
note: mismatched types ‘int’ and ‘long unsigned int’
The template parameter for the size of a std::array is of type std::size_t. However, here you require supplying an int for DIM in the function definition. This is probably an issue for the template deduction rules.

unpacking values of an array as parameters to a variadic function

I am trying (at compile time) to unpack integers as arguments to a variadic function. The idea would be to have those values packed in an array or in a std::index_sequence (c++14) at compile time. I have tried to use some of the answers from older posts, but I find the example code unreadable for my level.
Here is a simple example with the functionality that I need to implement in a code that I am writing, in this case attempting to use std::make_index_sequence. I do not necessarily need to use the latter. The problem is that the values of the sequence are not unpacked as arguments to the variadic function:
#include <cstdio>
#include <iostream>
#include <utility>
using namespace std;
void print(const int &val){
cout << val << endl;
}
template<typename ...S> void print(const int &val, const S&... others)
{
print(val);
print(others...);
}
template<size_t n> void printNumbers(){
std::make_index_sequence<n> a;
print(a);
}
int main(){
printNumbers<6>();
}
The output from GCC8:
tet.cc: In instantiation of ‘void printNumbers() [with long unsigned int n = 6]’:
tet.cc:25:19: required from here
tet.cc:20:8: error: no matching function for call to ‘print(std::make_index_sequence<6>&)’
print(a);
~~~~~^~~
tet.cc:8:6: note: candidate: ‘void print(const int&)’
void print(const int &val){
^~~~~
tet.cc:8:6: note: no known conversion for argument 1 from ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’} to ‘const int&’
tet.cc:12:30: note: candidate: ‘template<class ... S> void print(const int&, const S& ...)’
template<typename ...S> void print(const int &val, const S&... others)
^~~~~
tet.cc:12:30: note: template argument deduction/substitution failed:
tet.cc:20:9: note: cannot convert ‘a’ (type ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’}) to type ‘const int&’
std::make_index_sequence<6> is an alias for:
std::integer_sequence<std::size_t, 0, 1, 2, 3, 4, 5>
and this is the type of the expression a that is the argument of the function call print(a). Your print function expects individual values, not an std::integer_sequence.
To make your implementation work, you should first deduce the indices, and only then use them as arguments for print:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
print(Is...);
}
template <std::size_t N>
void printNumbers()
{
printNumbers(std::make_index_sequence<N>{});
}
In c++17 you could remove the intermediate print function and just say:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
(print(Is), ...);
}
In c++20 you can both create an index sequence and deduce its indices within a single function:
template <std::size_t N>
void printNumbers()
{
[] <std::size_t... Is> (std::index_sequence<Is...>)
{ (print(Is), ...); }(std::make_index_sequence<N>{});
}
DEMO
As an addendum to the Piotr Skotnicki's answer, I propose a C++14 way to avoid the recursive print().
Not so elegant as the C++17 solution, based on template folding, but equally permit to avoid the use of recursion (and consider that template recursion is usually strict limited by compilers, so the recursive solution works but not when N exceed the recursion limit).
You have to write the printNumber() function as usual, passing a std::make_index_sequence<N> (that inherit from std::index_sequence<0, 1, ...., N-1> aka std::integer_sequence<std::size_t, 0, 1, ..., N-1>) to another function
template <std::size_t N>
void printNumbers ()
{ printNumbers2(std::make_index_sequence<N>{}); }
but in the printNumbers2() you can avoid to call the recursive print() and you can call the print() that effectively call std::cout inside the initialization of an unused array
template <std::size_t ... Is>
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (print(Is), 0)... };
}
You can also avoid both print() functions printing directly in printNumbers2()
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (std::cout << val << std::endl, 0)... };
}
You can do the same in the C++17/C++20 template folding solutions.
In C++11 this solution doesn't works but only because std::make_integer_sequence and std::index_sequence are introduced from C++11.
If you write a C++11 surrogate for std::make_integer_sequence and std::index_sequence, you can adapt this solution also to C++11.

How to pass a template method as a template argument?

I would like to pass a template method as a template argument.
I don't understand why I am getting this error:
no known conversion for argument 1 from '<unresolved overloaded function type>' to 'void (B::*&&)(int&&, double&&)
Here is the code:
struct A {
template <class Fn, class... Args>
void f(Fn&& g, Args&&... args) {
g(std::forward<Args>(args)...);
}
};
struct B {
template <class... Args>
void doSomething(Args&&... args) {}
void run() {
A a;
a.f(&doSomething<int, double>, 1, 42.); // error here
}
};
int main() {
B b;
b.run();
return 0;
}
Any ideas?
The root cause for the error is that you need an object to call the member function. However, with current code, the error is not that straightforward.
Change calling site to
a.f(&B::doSomething<int, double>, 1, 42.)
And you will see much better error:
error: must use '.' or '->' to call pointer-to-member function in 'g
(...)', e.g. '(... ->* g) (...)'
doSomething is a member function, as such, it cannot be called without an object, which you are trying to do
g(std::forward<Args>(args)...);
^
where is the instance?
One solution to this is to wrap doSomething in a lambda:
a.f([](B& instance, int a, int b) { instance.doSomething(a, b); }, *this, 1, 42.);
If you can use C++17, you could also use std::invoke:
template <class Fn, class... Args>
void f(Fn&& g, Args&&... args) {
std::invoke(g, std::forward<Args>(args)...);
}
and then when calling f:
a.f(&B::doSomething<int, double>, this, 1, 42.);

How to create a perfect forwarding constructor for a tuple-like variadic class

I am trying to create thing similar to tuple, but I have come across a problem for writing my constructor.
Here is the code:
#include <tuple>
template <typename... Ts>
struct B {
template <typename... ArgTypes>
explicit B(ArgTypes&&... args)
{
static_assert(sizeof...(Ts) == sizeof...(ArgTypes),
"Number of arguments does not match.");
}
};
struct MyType {
MyType() = delete;
MyType(int x, const char* y) {}
};
int main()
{
B <int, char> a{2, 'c'}; // works
B <int, bool, MyType, char> b{2, false, {4, "blub"}, 'c'}; // fails
std::tuple<int, bool, MyType, char> t{2, false, {4, "blub"}, 'c'}; // works
}
Now, this works ok if pass simple types as initializers, but it does not, if I try to pass arguments in a brace-enclosed initializer list for non-trivial object.
GCC-4.7 emits the following:
vararg_constr.cpp:21:67: error: no matching function for call to 'B<int, bool, MyType, char>::B(<brace-enclosed initializer list>)'
vararg_constr.cpp:21:67: note: candidates are:
vararg_constr.cpp:6:14: note: B<Ts>::B(ArgTypes&& ...) [with ArgTypes = {}; Ts = {int, bool, MyType, char}]
vararg_constr.cpp:6:14: note: candidate expects 0 arguments, 4 provided
Clang-3.1 the following:
vararg_constr.cpp:21:40: error: no matching constructor for initialization of
'B<int, bool, MyType, char>'
B <int, bool, MyType, char> b{2, false,{4, "blub"}, 'c'}; // fails
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
vararg_constr.cpp:6:14: note: candidate constructor not viable: requires 2
arguments, but 4 were provided
explicit B(ArgTypes&&... args)
Ok, now what makes me very, very curious is that it works for tuple! According to the Standard (20.4.2.1) it has a constructor, that looks pretty much like mine.
template <class... Types>
class tuple {
public:
// ...
template <class... UTypes>
explicit tuple(UTypes&&...);
// ...
};
When constructing the tuple object in the same way, it works!
Now I would like to know:
A) What the hell? Why is std::tuple so special, and why don't compilers deduce the correct number of arguments?
B) How can I make this work?
A) Why should the Compiler know, that {4, "blub"} is of type MyType and not tuple<int, const char*>?
B) Change ArgTypes to Ts in the constructor:
explicit B(Ts&&... args)
Tuple does also have the following constructor:
explicit constexpr tuple(const _Elements&... __elements);
EDIT: The point is, that the constructor with const& is called and not that with the R-Values. Consider the following:
template <typename... Ts>
struct B {
explicit B(const Ts&... elements) { std::cout << "A\n"; }
template<typename... As,
typename = typename std::enable_if<sizeof...(As) == sizeof...(Ts)>::type>
explicit B(As&&... elements) { std::cout << "B\n" ;}
};
int main()
{
MyType m {1, "blub"};
B<int, char> a{2, 'c'}; // prints B
B<bool, MyType, char> b{false, {4, "blub"}, 'c'}; // prints A
B<bool, MyType, MyType>c{false, {4, "blub"}, std::move(m)}; // prints A
}