Howard Howard - 3 months ago 19
C++ Question

Template Type != Deduced type

I'm attempting to give a friendly name to a type as a template typename because I need to use the name in a few places within the function. The type is being deduced based on the number of other template arguments in the parameter pack, as shown below:

#include <cassert>
#include <functional>
#include <type_traits>

template < typename ... TArgs, typename Functor = std::function< std::conditional_t< sizeof...(TArgs) == 0, int (), int (TArgs...) > > >
void DoStuff(const Functor & func, TArgs ... args) {
if constexpr (sizeof...(TArgs) == 0)
assert(typeid(Functor).hash_code() == typeid(std::function<int ()>).hash_code());
else
assert(typeid(Functor).hash_code() == typeid(std::function<int (TArgs...)>).hash_code());
}

int main(int argc, char * argv[]) {
DoStuff([] () { return 5; });
DoStuff([] (int a) { return a; });

return 0;
}


This compiles just fine, but both assertions fail because the alias
Functor
is not actually a
std::function<>
. On the other hand, if I change the code to repeat the definition of
Functor
in the
typeid
calls it works perfectly, like below:

#include <cassert>
#include <functional>
#include <type_traits>

template < typename ... TArgs, typename Functor = std::function< std::conditional_t< sizeof...(TArgs) == 0, int (), int (TArgs...) > > >
void DoStuff(const Functor & func, TArgs ... args) {
if constexpr (sizeof...(TArgs) == 0)
assert(typeid(std::function< std::conditional_t< sizeof...(TArgs) == 0, int (), int (TArgs...) > >).hash_code() == typeid(std::function<int ()>).hash_code());
else
assert(typeid(std::function< std::conditional_t< sizeof...(TArgs) == 0, int (), int (TArgs...) > >).hash_code() == typeid(std::function<int (TArgs...)>).hash_code());
}

int main(int argc, char * argv[]) {
DoStuff([] () { return 5; });
DoStuff([] (int a) { return a; });

return 0;
}


Why is the first declaration (using the
typname Functor = ...
) incorrect? Is there a different way to make that alias? Note that in answering the second question, it is OK if the solution is a const expression, the examples are only not
constexpr
because of the use of
typeid
.

Answer Source

Why is the first declaration (using the typename Functor = ...) incorrect?

You're providing a default type for the template parameter Functor. But the default type is only used if the type is not otherwise specified or deduced. In this case, template deduction will deduce Functor in both cases to be whatever the unique type of the lambda is that it is invoked with (while Args... deduces as an empty pack).

This is similar to default function arguments not being used when they are provided.

I'm not sure what your assert()s are supposed to accomplish, but if you're checking types you should use static_assert.