Variadic template deduction using type alias - c++

I have a program like this:
template<typename ...Args>
using Function = void(*)(Args *...);
template<typename ...Args>
void DoThing(Function<Args...> func) { }
void IntFunction(int *i) { }
int main(int argc, char *argv[]) {
DoThing(IntFunction);
}
When I run the program I get this error
$ clang++ -std=c++14 template.cpp
template.cpp:12:3: error: no matching function for call to 'DoThing'
DoThing(IntFunction);
^~~~~~~
template.cpp:7:6: note: candidate template ignored: substitution failure [with Args = int]
void DoThing(Function<Args...> func) { }
^
1 error generated.
But if I compile it using g++ I don't get any errors.
It appears that clang is having trouble deducing the variadic template parameters when used in a type alias. If I replace the variadic parameters with standard ones then I don't get the error anymore.
Which compiler is giving me the proper result? And why would I not be allowed to do this?

Can be reduced to
template <typename... T>
using funptr = void(*)(T...);
template <typename... T>
void f(funptr<T...>) {}
template void f(void(*)());
This is valid code; if we substitute funptr<T...> by the corresponding pack expansion, Clang suddenly doesn't complain anymore.
Reported as #25250.

Related

Default template argument deducing to the wrong type in a conditional

I have the following testcase:
#include <type_traits>
template <typename Ty_>
using conditional_ref =
typename std::conditional<std::is_fundamental<Ty_>::value, Ty_, Ty_ const&>::type;
template <typename Ty_, typename Ty2_ = conditional_ref<Ty_>>
inline void f(Ty2_ arg1) {
static_assert(std::is_same<Ty2_, conditional_ref<Ty_>>::value, "uh-oh!");
}
int main() {
struct A {};
A a{};
double b{};
//f(a); // Cannot deduce parameter (expected)
//f(b); // Cannot deduce parameter (expected)
f<decltype(a)>(a); // uh-oh!
f<decltype(b)>(b); // OK
f<decltype(a), conditional_ref<decltype(a)>>(a); // OK
f<decltype(b), conditional_ref<decltype(b)>>(b); // OK
return 0;
}
In this, f<decltype(a)> deduces to f<A, A> instead of the expected f<A, A const&>.
I have tried in clang-10, gcc-9, and Visual Studio 2019; all of these give the same result, so I think I'm doing something wrong.
Example output from clang-10:
$ clang-10 test.cpp
test.cpp:9:3: error: static_assert failed due to requirement 'std::is_same<A, const A &>::value' "uh-oh!"
static_assert(std::is_same<Ty2_, conditional_ref<Ty_>>::value, "uh-oh!");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:19:3: note: in instantiation of function template specialization 'f<A, A>' requested here
f<decltype(a)>(a); // uh-oh!
^
1 error generated.
Why is the template deduction not what I expect here?
How do I rewrite f() so that it accepts a Ty_ when passed a fundamental type, and a Ty_ const& otherwise? Preferably while only having to pass the type of the argument once, like f<decltype(a)>(a).
Ty2_ is deducible, so default is not used at all. you might use directly:
template <typename Ty_>
void f(conditional_ref<Ty_> arg1) {
static_assert(std::is_same<decltype(arg1), conditional_ref<Ty_>>::value, "uh-oh!");
}
Demo
Type of Ty2_ is deduced by provided argument of function call. You can disable this deduction by
void f(std::type_identity_t<Ty2_> arg1)
then default template parameter type will be used.

How to force template substitution failure unless T::answer is in integral type?

I have the following code:
#include <type_traits>
struct SA {};
struct SB { static const int answer = 42; };
const int SB::answer;
template <typename T>
int F() {
return T::answer;
}
int main(int argc, char **argv) {
(void)argc; (void)argv;
// F<SA>(); // I want to make this a template substitution failure.
return F<SB>(); // This should still work.
}
I want to make the call F<SA>() become a template substitution failure. I've tried changing int F to typename std::enable_if<std::is_integral<T::answer>::value, int>::type F, but I got the following error:
$ g++ --versiong++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
$ g++ -W -Wall -Wextra -Werror -std=c++0x -fno-diagnostics-show-caret ei.cc && ./a.out
ei.cc:8:55: error: to refer to a type member of a template parameter, use ‘typename T:: answer’ [-fpermissive]
ei.cc: In function ‘int main(int, char**)’:
ei.cc:16:20: error: no matching function for call to ‘F()’
ei.cc:16:20: note: candidate is:
ei.cc:8:76: note: template<class T> typename std::enable_if<std::is_integral<typename T::answer>::value, int>::type F()
ei.cc:8:76: note: template argument deduction/substitution failed:
ei.cc: In substitution of ‘template<class T> typename std::enable_if<std::is_integral<typename T::answer>::value, int>::type F() [with T = SB]’:
ei.cc:16:20: required from here
ei.cc:8:76: error: no type named ‘answer’ in ‘struct SB’
This is trying to look for a type named answer in struct SB, but I want to make it look for an integral field named answer in struct SB.
I find putting the enable_if check in the template arguments makes your interface a bit clearer:
template <typename T,
std::enable_if_t<std::is_integral<decltype(T::answer)>::value>* = nullptr>
int F() {
return T::answer;
}
Or even clearer, using R. Martinho Fernandes' Remastered enable_if.
namespace detail {
enum class enabler {};
}
template <typename Condition>
using EnableIf = std::enable_if_t<Condition::value, detail::enabler>;
template <typename T, EnableIf<std::is_integral<decltype(T::answer)>>...>
int F() {
return T::answer;
}
Adding decltype(...) seems to work: changing int F to typename std::enable_if<std::is_integral<decltype(T::answer)>::value, int>::type F.

How to test lambda in C++11

Normally, to test a if a pointer points to function, use std::is_function is enough.
However, it cannot work with lambda. Since lambda is an object with operator().
Now I have to use both is_function and is_object to check if one works like function, as below:
std::is_function<decltype(f)>::value || std::is_object<decltype(f)>::value
So I'm wondering if there is a better way to test if one is lambda or not?
EDIT:
Related code:
template<typename Func>
void deferJob(Func f, int ms=2000)
{
if(! std::is_function<decltype(f)>::value
&& ! std::is_object<decltype(f)>::value){
qDebug()<<"Not function!";
return;
}
QTimer* t = new QTimer;
t->setSingleShot(true);
QObject::connect(t, &QTimer::timeout,
[&f, t](){
qDebug()<<"deferJob";
f();
t->deleteLater();
});
t->start(ms);
}
EDIT2:
Similar question: C++ metafunction to determine whether a type is callable
So here are some thoughts that may or may not be helpful.
To create a type_trait that works for functors, lambdas and traditional functions, I think I would look into seeing if the template argument is convertible into a std::function<void()>. I think that would cover most bases in a clear way.
As we've mentioned in the comments, you can't test a template argument like the way you are doing. The f() later in the function will cause a compile error, and so you'll never have the opportunity to see the runtime error.
You can try to do something with std::enable_if. You'd need to create template specializations so that SFINAE can function to choose the correct implementation. This would use that type_trait that I mentioned in bullet 1.
If you did this, you could make the implementation of the other template to be a static_assert to create a "better" error message.
That being said, the compiler error messages aren't that bad in the first place. (At least in clang and gcc. I haven't looked as msvc).
This doesn't get you a great error message, but it does get you a different one:
#include <cassert>
#include <functional>
#include <type_traits>
template <typename Func>
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
}
void normal_function() {}
int main() {
deferJob([]() {}); // works
deferJob(&normal_function); // works
deferJob(3); // compile time error
}
In Clang, I get an error that looks like:
foo.cc:15:2: error: no matching function for call to 'deferJob'
deferJob(3); // compile time error
^~~~~~~~
foo.cc:6:25: note: candidate template ignored: disabled by 'enable_if' [with Func = int]
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
In GCC, I get an error that looks like:
foo.cc: In function ‘int main()’:
foo.cc:15:12: error: no matching function for call to ‘deferJob(int)’
deferJob(3); // compile time error
^
foo.cc:15:12: note: candidate is:
foo.cc:7:1: note: template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int)
deferJob(Func f, int ms=2000) {
^
foo.cc:7:1: note: template argument deduction/substitution failed:
foo.cc: In substitution of ‘template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int) [with Func = int]’:
foo.cc:15:12: required from here
foo.cc:7:1: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
We could go one step further (although doing it this way makes it hard to extend further) and add an additional function:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
static_assert(false, "You should pass a function");
}
This causes clang to report (at compile time):
foo.cc: In function ‘typename std::enable_if<(! std::is_convertible<Func, std::function<void()> >::value)>::type deferJob(Func, int)’:
foo.cc:14:2: error: static assertion failed: You should pass a function
static_assert(false, "You should pass a function");
But sadly, it doesn't give a stack trace, so I would find this far less helpful than any of the earlier messages.
And finally, we could also replace that static assert with your runtime message:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
qDebug() << "Not function!";
}

Why can't I manually provide the template arguments?

I have a variadic template function f. This compiles fine (using g++ -std=c++11 and possibly using c++0x):
#include <tuple>
template<int ...>
struct seq { };
template <typename ...T, int ...S>
void f(std::tuple<T...> arg, seq<S...> s) {
// ... do stuff
}
int main() {
f<int>(std::tuple<int>(10), seq<0>());
return 0;
}
The compiler automatically fills in the int ...S that works.
However, I can't seem to manually provide the integer arguments:
int main() {
f<int, 0>(std::tuple<int>(10), seq<0>());
return 0;
}
Output:
/tmp/t.cpp: In function ‘int main()’: /tmp/t.cpp:12:42: error: no
matching function for call to ‘f(std::tuple, seq<0>)’
/tmp/t.cpp:12:42: note: candidate is: /tmp/t.cpp:7:6: note:
template void f(std::tuple<_TElements ...>,
seq) /tmp/t.cpp:7:6: note: template argument
deduction/substitution failed:
I believe I've read that technically there should only be one variadic template parameter pack provided to a template function (and in the first case, it is completely determined by context), so that explains it(?).
For debugging, is there a way in GCC to output the expansion used for ...S to stderr or stdout? It would be very useful for debugging things like this when they don't compile at first.
I know no way to specify two template argument packs manually. Since a template pack may contain arbitrarily many arguments, there is no way for the compiler to know when you meant the first one to stop and the second one to start. Automatic or partially automatic deduction seems to work, though, but I am not sure if this is just some generousity of g++...
I am not sure what you are actually trying to do, but I bet that you do not need both template packs at the same time. You might introduce one layer of indirection, e.g.,
template <typename ... Tuple_Elements>
void do_something_with_single_value(std::tuple<Tuple_Elements...> arg, int s) {
// ... do stuff
}
template <typename Tuple_Type, int ...S>
void f(Tuple_Type arg, seq<S...> s) {
// ... do stuff which needs all S at the same time
// ... call do_something_with_single_value in compile time loop to access tuple elements
}
Perhaps your signature is a hint that your function has too many responsibilities. Try to create smaller functions with clear responsibilities.
There is a way to output the arguments deduced for T and S, but only if the compiler can determine a match. For this, you would need to provoke an error at compile time:
template <typename ...T, int ...S>
void f(std::tuple<T...> arg, seq<S...> s) {
static_assert(std::tuple_size<std::tuple<T...>>::value < 0, "Provoked error message");
// ... do stuff
}
This would generate the following output in your working example:
stack.cpp: In function ‘void f(std::tuple<_Elements ...>, seq<S ...>) [with T = {int}, int ...S = {0}]’:
stack.cpp:15:34: instantiated from here
stack.cpp:10:2: error: static assertion failed: "Provoked error message"
For getting stderr for types and values in situations like this I, personally, find it best to force a compile error for the situation you're interested in.
So in this situation we want to know exactly what the type and integer packs for the first example where it compiles correctly. If we cause a compile fail before instantiation of the function then the compile error will refer to the function without the info we want:
template <class ... T, int ... S>
void f (std::tuple<T...> arg, seq<S...> s)
{
foo
}
In function 'void f(std::tuple<_Elements ...>, seq<S ...>)':
error: 'foo' was not declared in this scope
demo
So now let's fail compilation during instantiation. This means that we'll have to write something that could make sense for some types of T..., but not for our instantiation. I usually write a function like force_fail to make this easy to repeat:
template <class T, class ...>
int force_compile_fail ()
{
return T::force_compile_fail;
}
template <class ... T, int ... S>
void f (std::tuple<T...> arg, seq<S...> s)
{
force_compile_fail<T...>();
}
In instantiation of 'int force_compile_fail() [with T = int;
= {}]':
required from 'void f(std::tuple<_Elements ...>, seq<S ...>) [with T = {int}; int ...S = {0}]'
required from here
error: 'force_compile_fail' is not a member of 'int'
return T::force_compile_fail;
demo

Passing function template specializations to a variadic template function

I have no problem passing the address of a function template specialization to a regular template function:
template <typename T>
void f(T) {}
template <typename A, typename B>
void foo(A, B) {}
int main()
{
foo(&f<int>, &f<float>);
}
However, when I try to pass the same specializations to a variadic template:
template <typename T>
void f(T) {}
template <typename... A>
void bar(A...) {}
int main()
{
bar(&f<int>, &f<float>);
}
I get the following compiler errors with GCC (I tried 4.6.1 and 4.7.0):
test.cpp: In function 'int main()':
test.cpp:9:27: error: no matching function for call to 'bar(<unresolved overloaded function type>, <unresolved overloaded function type>)'
test.cpp:9:27: note: candidate is:
test.cpp:5:6: note: template<class ... A> void bar(A ...)
test.cpp:5:6: note: template argument deduction/substitution failed:
Why am I getting these errors?
Looks like it might be a bug in GCC that is possibly fixed in GCC 4.6.2 (I say possibly because it's not exactly the same, but does involve getting the address of a variadic template function).