Why this code doesn't compile ?
#include <iostream>
#include <typeinfo>
template <typename ...Ts>
void f();
template <typename T>
void f() {
std::cout << typeid(T).name() << std::endl;
}
template <typename T, typename U, typename ...Ts>
void f() {
std::cout << typeid(T).name() << ", ";
f<U, Ts...>();
}
int main(int argc, char** argv)
{
f<int, float, char>();
}
MSVC compiler error:
error C2668: 'f': ambiguous call to overloaded function
Expected output:
int, float, char
Side question: would there be a more modern way to do the same thing ?
EDIT
I've found a way to accept zero template pack:
#include <typeinfo>
#include <iostream>
#include <type_traits>
template <typename ...Ts>
using is_empty_pack = std::enable_if_t<sizeof ...(Ts) == 0>;
template <typename ...Ts, typename = is_empty_pack<Ts...>>
void f() {}
template <typename T, typename ...Ts>
void f() {
std::cout << typeid(T).name();
if constexpr (sizeof ...(Ts) > 0) std::cout << ", "; else std::cout << std::endl;
f<Ts...>();
}
int main(int argc, char *argv[])
{
f<>();
f<int>();
f<int, float>();
}
Any other suggestion?
Compiling with g++ gives a pretty clear explanation of what's happening:
prog.cc: In function 'int main(int, char**)':
prog.cc:20:24: error: call of overloaded 'f<int, float, char>()' is ambiguous
20 | f<int, float, char>();
| ~~~~~~~~~~~~~~~~~~~^~
prog.cc:5:6: note: candidate: 'void f() [with Ts = {int, float, char}]'
5 | void f();
| ^
prog.cc:13:6: note: candidate: 'void f() [with T = int; U = float; Ts = {char}]'
13 | void f() {
You've provided three different templated functions f, two of which could match what you've written here.
EDIT: Maybe you thought the first one was a declaration and the other two are specializations, but that's not how templates work. Specialization means specializing the type or value of a particular template argument, not specializing the number of template arguments.
Deleting
template <typename ...Ts>
void f();
will make the program compile and run with the expected behavior.
Related
I am trying to use concepts to overload templated functions whose argument depends on the template parameter. Unfortunately this fails on gcc, reporting an ambiguous overload.
In my project I have code that compiles in gcc 9.3 and fails in gcc 12.2. But in trying to minimize the code, I've ended up with this, which fails on both gcc versions, but works in clang 15.0.0:
#include <type_traits>
#include <iostream>
struct A
{
using value_type = int;
};
struct B
{
using value_type = int;
};
template<typename Candidate>
concept something = requires {
typename Candidate::value_type;
};
template<typename Candidate>
concept something_specific = something<Candidate> && std::is_same_v<Candidate, A>;
template<something T>
void foo()
{
std::cout << "Something" << std::endl;
}
template<something_specific T>
void foo()
{
std::cout << "Something specific" << std::endl;
}
template<something T>
void bar(const typename T::value_type& s)
{
std::cout << "Something: " << s << std::endl;
}
template<something_specific T>
void bar(const typename T::value_type& s)
{
std::cout << "Something specific: " << s << std::endl;
}
int main()
{
foo<B>(); // works
foo<A>(); // works
bar<B>(1); // works
bar<A>(2); // ambiguous overload
return 0;
}
See godbolt, the error message is as expected:
<source>:48:11: error: call of overloaded 'bar<A>(int)' is ambiguous
48 | bar<A>(2); // ambiguous overload
| ~~~~~~^~~
<source>:32:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
32 | void bar(const typename T::value_type& s)
| ^~~
<source>:37:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
37 | void bar(const typename T::value_type& s)
| ^~~
I expected the something_specific overload to be chosen, because the concept that's restricting the template parameter is more specific. We can see this is true for foo already, otherwise it would be ambiguous as well.
Of course I can make it work with a requires clause, but I expected it to work as-is. So I'd like to understand whether this is just not supported or if it's a compiler issue. Is clang correct in compiling this code?
The following program does not compile:
#include <utility>
#include <iostream>
#define N 4
template <unsigned int I>
unsigned int g() { return I; }
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...> = std::make_integer_sequence<unsigned int, N>{})
{
return (g<I>() + ...);
}
int main()
{
std::cout << f() << std::endl;
return 0;
}
Test it live on Coliru.
With gcc the error is
main.cpp: In function 'unsigned int f(std::integer_sequence<unsigned
int, I ...>) [with unsigned int ...I = {}]':
main.cpp:17:18: error: could not convert
'std::make_integer_sequence<unsigned int, 4>{}' from
'integer_sequence<[...],'nontype_argument_pack' not supported by
dump_expr>' to
'integer_sequence<[...],'nontype_argument_pack' not supported by
dump_expr>'
A similar conversion error is reported with clang++:
error: no viable conversion from 'std::make_integer_sequence<unsigned
int, 4>' (aka '__make_integer_seq<integer_sequence, unsigned int,
4U>') to 'std::integer_sequence'
Strangely enough, however, if I remove the default parameter, and pass the same expression to f, the program compiles and gives the corrected output:
#include <utility>
#include <iostream>
#define N 4
template <unsigned int I>
unsigned int g() { return I; }
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
return (g<I>() + ...);
}
int main()
{
std::cout << f(std::make_integer_sequence<unsigned int, N>{}) << std::endl;
return 0;
}
See it live on Coliru.
What's the problem/difference with the first code?
I admit, I do not understand the error message. The reason the second version compiles but not the first is that template parameters cannot be deduced from default arguments but from function parameters. Consider this simpler example:
#include <utility>
#include <iostream>
template <unsigned int>
struct foo {};
template <unsigned int x>
foo<x> make_foo(){ return {};}
template <unsigned int x>
unsigned int f(foo<x> = make_foo<4>())
{
return 42;
}
int main()
{
std::cout << f() << std::endl;
return 0;
}
Here the error is a little more descriptive:
<source>: In function 'int main()':
<source>:18:18: error: no matching function for call to 'f()'
18 | std::cout << f() << std::endl;
| ^
<source>:11:14: note: candidate: 'template<unsigned int x> unsigned int f(foo<x>)'
11 | unsigned int f(foo<x> = make_foo<4>())
| ^
<source>:11:14: note: template argument deduction/substitution failed:
<source>:18:18: note: couldn't deduce template parameter 'x'
18 | std::cout << f() << std::endl;
| ^
The main purpose of make_integer_sequence<unsigned int,N> is to make the transition from a single N to the pack in std::integer_sequence<unsigned int, I...> as you do it in your second example.
With a level of indirection you avoid that the caller must pass the parameter:
// ...
template <unsigned int... I>
unsigned int f(std::integer_sequence<unsigned int, I...>)
{
return (g<I>() + ...);
}
template <unsigned int X = N>
unsigned int f_wrap()
{
return f(std::make_integer_sequence<unsigned int,N>{});
}
int main()
{
std::cout << f_wrap() << std::endl;
return 0;
}
Live Demo
I try to implement a function f: (std::function -> int) which will pass 1s into input_functor
with c++ variadic template.
Let input_functor be g.
For example:
If g is std::function<int(int,int)>, then f return g(1, 1).
If g is std::function<int(int,int,int)>, then f return g(1, 1, 1).
If g is std::function<int(int,int,int,int)>, then f return g(1, 1, 1, 1).
#include <functional>
#include <iostream>
template <typename T, typename... Args>
int apply(std::function<int(T, Args...)> func) {
auto tmp = [func](Args... args) {
return func(1, args...);
};
return apply(tmp);
}
template <typename T>
int apply(std::function<int(T)> func) {
return func(1);
}
int main() {
std::function<int(int, int)> f = [](int a, int b) {
return a + b;
};
std::cout << apply(f) << "\n";
return 0;
}
The compiler (clang++) error msg is that it cannot match candidates.
main.cpp:9:12: error: no matching function for call to 'apply'
return apply(tmp);
^~~~~
main.cpp:21:18: note: in instantiation of function template specialization 'apply<int, int>' requested here
std::cout << apply(f) << "\n";
^
main.cpp:5:5: note: candidate template ignored: could not match 'function<int (type-parameter-0-0, type-parameter-0-1...)>' against
'(lambda at main.cpp:6:16)'
int apply(std::function<int(T, Args...)> func) {
^
main.cpp:13:5: note: candidate template ignored: could not match 'function<int (type-parameter-0-0)>' against '(lambda at main.cpp:6:16)'
int apply(std::function<int(T)> func) {
^
1 error generated.
You have 2 issues:
definition order:
template <typename T>
int apply(std::function<int(T)> func) {
return func(1);
}
should be place before the recursive function to allow to be visible and ends recursion.
lambda is not a std::function, so deduction don't happen
template <typename T, typename... Args>
int apply(std::function<int(T, Args...)> func) {
auto tmp = std::function{[func](Args... args) { // C++17 CTAD
return func(1, args...);
}};
return apply(tmp);
}
Demo C++17
As you are limited to C++11, you might create traits to know which std::function is needed:
template <typenate T, typename Discarded>
struct always_first
{
using type = T;
};
template <typenate T, typename Discarded> using always_first_t = typename always_first<T, Discarded>::type;
// possibly directly
// template <typenate T, typename Discarded> using always_first_t = T;
// but old compilers might have some issues with that construct as std::void_t
and then
std::function<int(always_first_t<int, Args>...)> tmp = /* your lambda */;
I have this code :
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;
template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;
template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }
template <typename T> void f(FloatingPoint<T>) {
std::cout << "floating point" << std::endl;
}
int main() {
f(5);
return 0;
}
This code does not compile :
prog.cc:12:28: error: redefinition of 'f'
template <typename T> void f(FloatingPoint<T>) {
^
prog.cc:10:28: note: previous definition is here
template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }
^
prog.cc:17:3: error: no matching function for call to 'f'
f(5);
^
prog.cc:12:28: note: candidate template ignored: substitution failure [with T = int]
template <typename T> void f(FloatingPoint<T>)
It is kind of weird because it understands that there is a substitution failure, but he does not want to take the first overloading.
So I know two ways to handle with this. The first thing is to put the enable_if either as an argument, or as a return type. Is there other way to handle this in a "pretty" way? Or have I to use one of the two solutions I wrote before?
The goal is to write clean code. I'd love to use concept from C++20 but it seems that it is not possible everywhere yet (I mean GCC, Clang, and MSVC) https://en.cppreference.com/w/cpp/compiler_support. Or this website is not up to date and both MSVC (2019), GCC 10 and clang 9 supports well concept?
I think your problem is that FloatingPoint and Integral directly depend on T. Re-organizing your code you can get the following. You can run the code here: https://onlinegdb.com/By-T4bBfB .
#include <iostream>
#include <type_traits>
template<typename T>
using isFloatingPoint = std::enable_if_t<std::is_floating_point_v<T>, bool>;
template<typename T>
using isIntegral = std::enable_if_t<std::is_integral_v<T>, bool>;
template <typename T, isIntegral<T> = true>
void f(T) {
std::cout << "integral" << std::endl;
}
template <typename T, isFloatingPoint<T> = true>
void f(T) {
std::cout << "floatingPoint" << std::endl;
}
int main() {
f(5);
f(5.0);
}
Alternatively, given that you are using C++17 you can use if constexpr. You say that is not scalable in the comments. In my opinion it is, but most likely I do not understand the constraints you are operating under. For this see https://godbolt.org/z/gLc7YE . if constexpr allows you to have only one function and even different return types pretty easily.
#include <type_traits>
#include <string>
template<typename T>
auto f(T){
if constexpr (std::is_floating_point_v<T>){
return 0.0f;
}
else if constexpr (std::is_integral_v<T>){
return 0;
} else {
return 0.0;
}
}
int main() {
static_assert(std::is_same_v<decltype(f(5)), int>, "ERROR 1");
static_assert(std::is_same_v<decltype(f(5.0)), float>, "ERROR 2");
static_assert(std::is_same_v<decltype(f(std::string{"JJ"})), double>, "ERROR 3");
return 0;
}
Finally I would also to note that having a different return type for the two f() in your code would solve your issue (run here the code: https://onlinegdb.com/By-9HZrMS):
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;
template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;
template <typename T> int f(Integral<T>) { std::cout << "integral" << std::endl; return 0;}
template <typename T> float f(FloatingPoint<T>) {
std::cout << "floating point" << std::endl;
return 0.0f;
}
int main() {
f(5);
f(5.0);
return 0;
}
Having the following piece of code:
#include <iostream>
#include <type_traits>
template <typename F,
typename = typename std::enable_if<
std::is_function< F >::value
>::type>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
fun(l); // line 23
//fun(l7); this will also fail even though l7 is a regular function
std::cout << std::is_function<decltype(l7)>::value ; // prints 1
}
I will get the following error:
main2.cpp: In function ‘int main()’:
main2.cpp:23:8: error: no matching function for call to ‘fun(main()::<lambda(int)>&)’
fun(l);
^
main2.cpp:8:5: note: candidate: template<class F, class> int fun(F)
int fun( F f )
^
main2.cpp:8:5: note: template argument deduction/substitution failed:
main2.cpp:5:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
typename = typename std::enable_if<
^
When I comment out the std::enable_if template parameter then it compiles and runs just fine. Why?
From cppreference:
Checks whether T is a function type. Types like std::function, lambdas, classes with overloaded operator() and pointers to functions don't count as function types.
This answer explains that you also need to use std::remove_pointer<F>::type as the type since functions are converted to pointers to functions when passing by value. So your code should look like this:
template <typename F,
typename = typename std::enable_if<
std::is_function<
typename std::remove_pointer<F>::type
>::value
>::type>
int fun( F f )
{
return f(3);
}
Another way to approach this problem is to write a more specific type trait. This one, for example, checks that the argument types are convertible and works for anything that's callable.
#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
template<class T, class...Args>
struct is_callable
{
template<class U> static auto test(U*p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template<class U> static auto test(...) -> decltype(std::false_type());
static constexpr auto value = decltype(test<T>(nullptr))::value;
};
template<class T, class...Args>
static constexpr auto CallableWith = is_callable<T, Args...>::value;
template <typename F,
std::enable_if_t<
CallableWith<F, int>
>* = nullptr
>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
std::cout << "fun(l) returns " << fun(l) << std::endl;
std::cout << CallableWith<decltype(l7), int> << std::endl; // prints 1
std::cout << CallableWith<decltype(l7), float> << std::endl; // prints 1 because float converts to int
std::cout << CallableWith<decltype(l7), const std::string&> << std::endl; // prints 0
}
Have a look at std::is_invocable which also covers lambdas in C++17 (std::is_callable does not exist).