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

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.

Related

What is the variadic function template overloading precedence rule?

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

print a list of numbers using template metaprogramming

I am unable to figure out why does following code not compile. Could you guys please help me?
#include <iostream>
template <typename T, typename ...Args>
void foo(T i, Args ... args){
std::cout << i << std::endl;
foo(args...);
}
template <typename T>
void foo(T i){
std::cout << i << std::endl;
std::cout << "end of list" << std::endl;
}
int main(){
foo(1, 2);
return 0;
}
I get the following error
function_parameter_pack.cpp: In instantiation of ‘void foo(T, Args ...) [with T = int; Args = {}]’:
function_parameter_pack.cpp:18:6: required from ‘void foo(T, Args ...) [with T = int; Args = {int}]’
function_parameter_pack.cpp:47:11: required from here
function_parameter_pack.cpp:18:6: error: no matching function for call to ‘foo()’
foo(args...);
~~~^~~~~~~~~
function_parameter_pack.cpp:16:6: note: candidate: template<class T, class ... Args> void foo(T, Args ...)
void foo(T i, Args ... args){
^~~
function_parameter_pack.cpp:16:6: note: template argument deduction/substitution failed:
function_parameter_pack.cpp:18:6: note: candidate expects at least 1 argument, 0 provided
foo(args...);
It seems error is happening during base case. But I am not able to understand why.
I tried following base-case also but got the same error
void foo(){
std::cout << "end of list" << std::endl;
}
Functions must be declared before they can be called. Change the order of functions.
Your special case is not a specialization of the base case, so the base case cannot see it. Because the template argument list is different, it is simply an overloaded function.
template <typename T>
void foo(T i){
std::cout << i << std::endl;
std::cout << "end of list" << std::endl;
}
template <typename T, typename ...Args>
void foo(T i, Args ... args){
std::cout << i << std::endl;
foo(args...);
}
You already got answers with a fix for the immediate problem you are facing. I want to add that your example isn't using the full power of fold expressions as they become available with C++17. If you can upgrade to a more recent standard, I strongly suggest you to do so.
You are using a fold expression here foo(args...) to recursively instantiate foos with different number of arguments. This isn't needed. Recursion was the "old" way to implent your code, fold expressions is the more modern way. You can mix them, but you don't need to:
#include <iostream>
template <typename ...Args>
void foo(Args ... args){
(std::cout << ... << args) << std::endl;
}
int main(){
foo(1, 2);
return 0;
}
Note that there is only a single instantiation of foo required. No recursion, hence no unnecessary functions that you don't really need (if you only ever call foo with 2 parameters, then you do not need a foo that takes 1).
Swap the order of the functions (the one taking multiple arguments calls the one taking one argument, so the single argument function must have been declared) and make the function that takes a single argument take the argument as T, not int - or else the template argument deduction will fail.
template <typename T>
void foo(T i) {
std::cout << i << std::endl;
std::cout << "end of list" << std::endl;
}
template <typename T, typename ...Args>
void foo(T i, Args... args){
std::cout << i << std::endl;
foo(args...);
}

Ambiguous call to overloaded function in variadic template function

I am using a variadic template function where the function parameters isn't the templated types.
I got a compilation error:
Error C2668 '_TailHelper': ambiguous call to overloaded function
Here it is the code snippet.
template <typename HEAD>
void _TailHelper(int) {
std::cout << typeid(HEAD).name() << std::endl;
}
template <typename HEAD, typename ... TAILS>
void _TailHelper(int x) {
_TailHelper<HEAD>(x);
_TailHelper<TAILS...>(x);
}
int main(){
_TailHelper<int,double>(2);
}
Both overloads match with single template argument, so you have to disable one. For example like that:
#include <iostream>
#include <typeinfo>
template <typename T>
void TailHelper(int) {
std::cout << typeid(T).name() << std::endl;
}
template <typename HEAD, typename ... TAILS>
typename std::enable_if<(sizeof...(TAILS) != 0)>::type
TailHelper(int x) {
TailHelper<HEAD>(x);
TailHelper<TAILS...>(x);
}
int main() {
TailHelper<int,double>(2);
}
The ambiguous call comes from this line:
_TailHelper<HEAD>(x);
this call matches both functions the first one, and the second function which its second parameter can refer to zero or more template parameters.
As alternative to recursion, you might "loop" over your variadic:
In C++17:
template <typename... Ts>
void PrintTypes() {
((std::cout << typeid(Ts).name() << std::endl), ...);
}
In previous version, it is less elegant, and you might use some trick as:
template <typename... Ts>
void PrintTypes() {
const int dummy[] = {0, ((std::cout << typeid(Ts).name() << std::endl), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
}

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.

variadic form is ambiguous with the nonvariadic form for a single argument

why in the following code compiler doesn't complain because of ambiguity?
template <typename T>
void print (const T& arg)
{
std::cout << arg << std::endl;
}
template <typename T, typename... Types>
void print (const T& firstArg, const Types&... args)
{
std::cout << firstArg << std::endl; // print first argument
print(args...); // call print() for remaining arguments
}
It's because overload resolution always favours a non-variadic function form over a variadic one.
The C++11 standard insists on that.