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...);
}
Related
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
}
I want to write a function that will do an operation based on the types, not arguments, of a function. As such, the function receives no template based arguments. The general gist is something like this:
#include <iostream>
void func() {
std::cout<<"End marker\n";
}
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(T)<<"\n";
func<T...>();
}
int main() {
func<int, int, int>();
}
Which, of course, doesn't compile. I've tried doing this:
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(T)<<"\n";
if( sizeof...(T)!=0 )
func<T...>();
}
This does not work, however. The func<T...> may not get evaluated, but it does need to be compilable.
Is there a way to do this that I'm missing?
You can make your original setup work by making func a "template function" (that doesn't actually use the template) like:
template<int = 0>
void func() {
std::cout<<"End marker\n";
}
template <typename Type, typename... T>
void func() {
std::cout<<"Type sizeof "<<sizeof(Type)<<"\n";
func<T...>();
}
And your second one can work by using if constexpr, so func<>() isn't compiled.
With fold-expression (in C++17), you might do:
template <typename... Ts>
void func()
{
((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), ...);
std::cout << "End marker\n";
}
That can even be done in C++11, but it would be more verbose:
template <typename... Ts>
void func()
{
const int dummy[] = {((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
std::cout <<"End marker\n";
}
Demo
You can make the non-template function func into a variadic template function which accepts zero template arguments. Then let SFINAE move away this template function when the number of arguments is not zero.
Following should work:
#include <iostream>
#include <type_traits>
template <typename... Ts>
typename std::enable_if<sizeof...(Ts) == 0>::type func() {
std::cout<<"End marker\n";
}
template <typename T, typename... Ts>
void func() {
std::cout << "Type sizeof " << sizeof(T) << "\n";
func<Ts...>();
}
int main() {
func<int, int, int>();
}
However, please note that:
(8) The validity of a template may be checked prior to any
instantiation. [ Note: Knowing which names are type names allows the
syntax of every template to be checked in this way. — end note ] The
program is ill-formed, no diagnostic required, if: [..] (8.3) every
valid specialization of a variadic template requires an empty template
parameter pack...
Source here
UPDATE
This would work too:
#include <iostream>
#include <type_traits>
void func() {
std::cout<<"End marker\n";
}
template <typename T, typename... Ts>
void func() {
std::cout << "Type sizeof " << sizeof(T) << "\n";
if constexpr (0 == sizeof...(Ts))
func();
else
func<Ts...>();
}
int main() {
func<int, int, int>();
}
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.
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.
I try to use std::enable_if with an unused and unnamed type parameter, in order to not distort the return type. However, the following code does not compile.
#include <iostream>
template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
The compiler says:
7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:
What is the problem here? How do I have to change the code to get it compile? The text book "Discovering Modern C++" explicitly encourages the use of std::enable_if with anonymous type parameters.
EDIT: I know that it works if I put std::enable_if into the return type. However, my intention is to get some more details why it does not work if I use it with anonymous type parameters. As I said, my text book encourages the variant using anonymous type parameters, so I am wondering why my code does not compile.
However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.
Default values do not participate in overload resolution, thus you are actually redefining the same function.
Let's simplify your example:
template<typename = int>
void f() {}
template<typename = void>
void f() {}
int main() {
f<>();
}
The code above does not compile, for it couldn't know what version of f you want to invoke.
In your case, if I invoke foo as foo<void, void>, I've almost the same problem.
The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.
Because of that, the code is ill-formed and the compiler correctly gives you an error.
As a side note, you can still have it working without using the std::enable_if_t in the return type.
As an example:
#include <type_traits>
#include <iostream>
template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, #T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:
It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.
You can put enable_if into the return type:
template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }
By the way, enable_if_t is available from C++14, so you might want to say typename std::enable_if<std::is_integral<T>::value, T>::type instead. Quite a mouthful.
But a little more idiomatic (and readable) would be to dispatch basing on the type:
template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }
template <typename T>
T foo(){
return foo_impl<T>(typename std::is_integral<T>::type{});
}
There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.
template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }
Your error is that you're using enable_if_t on the right of the equal sign.
You have to use it on the left
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
But this work with C++14.
In C++11 (your question is tagged C++11) you don't have enable_if_t.
The code become
#include <iostream>
#include <type_traits>
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}