What is the variadic function template overloading precedence rule? - c++

I'm using variadic function templates in the common recursive format and I need to change the behaviour of the function whenever I'm handling a vector.
If the functions templates were not variadic, overloading works well, but with variadic function templates, the overloading resolution seems to change when unpacking the argument pack.
Below some code to explain better what I mean.
#include <iostream>
#include <vector>
template<typename T>
void complexfun(T x) {
std::cout << "1 end" << std::endl;
}
template<typename T, typename... Args>
void complexfun(T x, Args... args) {
std::cout << "1 ";
complexfun(args...);
}
template<typename T>
void complexfun(std::vector<T> x) {
std::cout << "2 end" << std::endl;
}
template<typename T, typename... Args>
void complexfun(std::vector<T> x, Args... args) {
std::cout << "2 ";
complexfun(args...);
}
int main() {
std::vector<int> vint = {2, 3, 4};
float x1 = 9.4;
complexfun(vint); // output: 2 end -> OK
complexfun(vint, x1); // output: 2 1 end -> OK
complexfun(x1, vint); // output: 1 1 end -> WRONG: need 1 2 end
return 0;
}
In the execution of complexfun(x1, vint) we should have complexfun(vint), but it does not behave as the "standalone" call complexfun(vint).
Any help on why this is the case and how to fix it is greatly appreciated!

You need to declare template<typename T> void complexfun(std::vector<T>) before the function that is supposed to be using it.
Just swap the order of those function templates so you get:
template<typename T> // this function template
void complexfun(std::vector<T>) {
std::cout << "2 end" << std::endl;
}
template<typename T, typename... Args> // ...before this function template
void complexfun(T, Args... args) {
std::cout << "1 ";
complexfun(args...);
}
Demo

Related

Is there a way to partially specialize a template with parameter packs for a recursive function?

I'm trying to make a print function in C++ that takes in variable numbers of arguments and prints them each on their own line, like:
template<typename Ty, typename... Types>
void println(Ty cur_line, Types... other_lines)
{
std::cout << cur_line << '\n';
println(other_lines);
}
void println() { std::cout << std::flush; }
However, if Ty happens to be a std::vector<std::string>, I want to treat it differently (because I want to print every element of the vector on its own line). I looked into partial specialization, but there doesn't seem to be much that I could find on doing so with parameter packs. Here's what I tried:
template<typename Ty, typename... Types>
void println(Ty cur_line, Types... other_lines)
{
std::cout << cur_line << '\n';
println(other_lines);
}
template<typename... Types>
void println<std::vector<std::string>, Types...>
(std::vector<std::string> cur_line, Types... other_lines)
{
for (const auto& line : cur_line)
{
std::cout << line << '\n';
}
println(other_lines);
}
void println() { std::cout << std::flush; }
However, I'm getting an MSVC error C2768: "'println': illegal use of explicit template arguments".
Any suggestions or solutions would be warmly welcomed! For reference, I'm using Visual Studio 2019 Preview and its corresponding compiler version.
A simpler way would be to have a print function and overload that:
template < typename T >
void print(const T& line)
{
std::cout << line << '\n';
}
template < typename T >
void print(const std::vector<T>& line)
{
for (const auto& element : line)
{
print(element);
}
}
template<typename Ty, typename... Types>
void println(Ty cur_line, Types... other_lines)
{
print(cur_line);
println(other_lines);
}
void println() { std::cout << std::flush; }
You can do it like this:
/* main.cpp */
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <class T>
void PrintLine(const T &t)
{
cout << t << endl ;
}
template <class T>
void PrintLine(const vector<T> &t)
{
for(auto &obj : t ) PrintLine(obj);
}
template <class ... TT>
void PrintLines(const TT & ... tt)
{
( PrintLine(tt) , ... );
}
/* main() */
int main()
{
vector<string> list{"a","b","c"};
PrintLines(1,2,3,list);
return 0;
}
You can't partially specialize a function template, but you can overload it. Create a new overload of println that takes a std::vector<std::string> as the first argument and a general pack as the rest. Then do the special handling for the vector arg and forward the rest as before. Now your vectors will always be caught by the overload.

How do I realise a Template for a recursive function that allows many parameters of undefined datatypes in C++? [duplicate]

This question already has answers here:
How to make a variadic macro for std::cout?
(4 answers)
Closed 3 years ago.
I want to create a C++ Template for a function, that allows per default intinity arguments of any datatypes. I found some examples where they try to Code the printf function but it doesn't work (they included stdarg.h, i want something like that:)
//util.cpp
#include <iostream>
template<typename ...Args>
void debugPrint(Args... args)
{
// pseudo: foreach args as a:
std::cout << a;
// pseudo: endforeach
std::cout << std::endl;
}
//util.hpp
template<typename ...Args> //?
void debugPrint(Args...);
//app.cpp
#include "util.hpp"
int main()
{
debugPrint("Hallo", " ", "Welt", 1, 2, "\t", 2.3);
return 0;
}
Want the consoleoutput:
Hallo Welt12 [TAB] 2.3
Then there were an examplte with stdarg.h
#include <stdarg.h>
void tprintf(const char* format) // base function
{
std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive function
{
for (; *format != '\0'; format++) {
if (*format == '%') {
std::cout << value;
tprintf(format + 1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
So how? :(
Thanks for any answere <3
As with any recursive solution, you need a base case to terminate the recursion. In this situation the base case is when there are no arguments left to print, in which case you do nothing:
void debugPrint() { }
Then the recursive template function, which processes a single "first" argument and then recurses on the remaining variadic arguments:
template <typename FirstArg, typename ...Args>
void debugPrint(FirstArg arg, Args... args)
{
std::cout << arg;
debugPrint(args...);
}
Tying it all together:
#include <iostream>
void debugPrint() { }
template <typename FirstArg, typename ...Args>
void debugPrint(FirstArg arg, Args... args)
{
std::cout << arg;
debugPrint(args...);
}
int main()
{
debugPrint("Hallo", " ", "Welt", 1, 2, "\t", 2.3);
}
Outputs:
Hallo Welt12 2.3
You can use fold expressions to do that (C++17):
template<typename... Args>
void debugPrint(Args const&... args) {
(std::cout << ... << args);
}
It will expand args with a << between expanded expressions. There is no need for recursion.
Here's a live example.

How to define template function overload to match empty std::tuple<>?

I'd like to distinguish between empty and non-empty tuples, and I came up with the following solution (example):
#include <tuple>
#include <iostream>
template <typename ... Args>
void function(const std::tuple<Args...>& t)
{
std::cout << "empty tuple" << std::endl;
}
template <typename Head, typename ... Args>
void function(const std::tuple<Head, Args...>& t)
{
std::cout << "tuple of size: " << sizeof...(Args) + 1 << std::endl;
}
int main()
{
function(std::make_tuple()); // picks 1st function
function(std::make_tuple(1)); // picks 2nd function
function(std::make_tuple(1, 2, 3, '4')); // picks 2nd function
}
However, using variadic Args to match std::tuple<> is misleading for a reader, and I think that introducing Head in the 2nd overload is excessive. Is there a simple way to write an overload that matches std::tuple<> directly?
Did you try:
void function(const std::tuple<>& t) { ... }
?
Then you don't need to write Head out separately in the second function. Live example: http://coliru.stacked-crooked.com/a/1806c3a8a3e6b2d1.

Fallback function using ellipsis: can we force the size of the parameters pack?

Consider the following code:
#include <utility>
#include <iostream>
struct S {
template<typename T, typename... A>
auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) {
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f(...) {
std::cout << "has not f(int)" << std::endl;
}
};
struct T { void f(int) { } };
struct U { };
int main() {
S s;
s.f<T>(42); // -> has f(int)
s.f<U>(42); // -> has not f(int)
// oops
s.f<T>(); // -> has not f(int)
}
As shown in the example the third call to f works just fine, even if the number of arguments is wrong, for it's not wrong at all for the fallback function.
Is there a way to force the number of arguments when an ellipsis is involved that way?
I mean, can I check at compile time that the size of the arguments list is exactly 1, no matter if the main function or the fallback is chosen?
Good solutions are also the ones that only involves the first template function and result in hard-errors instead of soft-errors because of the size of the parameter pack.
Of course, it can be solved with several techniques without using variadic arguments. As an example: int/char dispatching on internal template methods; explicitly specify the arguments list; whatever...
The question is not about alternative approaches to do that, I already know them.
It's just to know if I'm missing something basic here or it's not possible and that's all.
If I understand correctly your issue, you may add a layer:
struct S {
private:
template<typename T, typename... A>
auto f_impl(A&&... args)
-> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) {
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f_impl(...) {
std::cout << "has not f(int)" << std::endl;
}
public:
template<typename T, typename A>
auto f(A&& args) { return f_impl<T>(std::forward<A>(arg)); }
};
With traits, you may do
template <typename T, typename ... Ts>
using f_t = decltype(std::declval<T>().f(std::declval<Ts>()...));
template <typename T, typename ... Ts>
using has_f = is_detected<f_t, T, Ts...>;
struct S {
template<typename T, typename... A>
std::enable_if_t<has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args)
{
std::cout << "has f(int)" << std::endl;
}
template<typename T, typename... A>
std::enable_if_t<!has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args) {
std::cout << "has not f(int)" << std::endl;
}
};
Demo
You can use a function (assert) that gets pointer to a function to deduce size of paramemters :
#include <utility>
#include <iostream>
template <typename...Args>
struct size_assert{
template <typename T,typename R,typename... Params>
constexpr static bool assert(R(T::*)(Params...) )
{
static_assert(sizeof...(Args) == sizeof...(Params),"Incorrect size of arguments!");
return true;
}
};
struct S {
template<typename T, typename... A, bool = size_assert<A...>::assert(&T::f)>
auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void())
{
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f(...) {
std::cout << "has not f(int)" << std::endl;
}
};
struct T { void f(int) { } };
struct U { };
int main() {
// std::cout <<fc(&f);
S s;
s.f<T>(42); // -> has f(int)
s.f<U>(42); // -> has not f(int)
// oops
s.f<T>(); // -> has not f(int)
}

Output all variadic template parameters

I am simply trying to output all of the parameters passed to a variadic function template. I tried this recursive approach:
#include <iostream>
template<typename TFirst, typename... Arguments>
void Output(TFirst first, Arguments... parameters)
{
std::cout << first << std::endl;
Output(parameters);
}
template<typename TFirst>
void Output(TFirst first)
{
std::cout << first << std::endl;
}
void Output()
{
// do nothing
}
int main()
{
Output(1.0f, 2);
}
but I get parameter packs not expanded with '...'. Can anyone explain what is wrong here?
--------- EDIT ------------
Here is the solution:
#include <iostream>
template<typename T>
inline void showArguments(T&& arg) { std::cout << arg << std::endl; }
template<typename First, typename... Rest>
inline void showArguments(First&& first, Rest&& ...rest)
{
showArguments(first);
showArguments(rest...);
}
int main()
{
showArguments("Hello", "World", 1, 2.3, false, true);
}
Should be
template<typename TFirst, typename... Arguments>
void Output(TFirst first, Arguments... parameters)
{
std::cout << first << std::endl;
Output(parameters...);
}
(note the trailing ... after the parameters)