Reputation: 2113
I'm trying to learn c++ template metaprogramming by implementing some functions. I know the solution to this specific problem has been provided on stackoverflow, what I'm interested in is understanding why this solution doesn't work. Here's the code:
template < std::size_t... Ns , typename... Ts >
auto tail_impl( std::index_sequence<Ns...> , std::tuple<Ts...> t )
{
return std::make_tuple( std::get<Ns+1u>(t)... );
}
template <class F, class... R >
tuple<R...> tail( std::tuple<F,R...> t )
{
return tail_impl( std::make_index_sequence<sizeof...(R)>() , t );
}
template<class X, class F, class... R>
constexpr bool check_for_type(tuple<F,R...> t) {
if constexpr(is_same<F,X>::value) {
return true;
}
return check_for_type<X>(tail(t));
}
template<class X>
constexpr bool check_for_type(tuple<> t) {
return false;
}
int main( int argc, const char *argv) {
auto t2 = make_tuple(4,"qw", 6.5);
double f = check_for_type<double>(t2);
return 0;
}
This template is supposed to check if a tuple contains an element of a certain type, but compiling it gives the following error:
> clang++ t.cpp -std=c++17
t.cpp:45:12: error: call to function 'check_for_type' that is neither visible in the
template definition nor found by argument-dependent lookup
return check_for_type<X>(tail(t));
^
t.cpp:45:12: note: in instantiation of function template specialization
'check_for_type<double, double>' requested here
t.cpp:45:12: note: in instantiation of function template specialization
'check_for_type<double, const char *, double>' requested here
t.cpp:66:16: note: in instantiation of function template specialization
'check_for_type<double, int, const char *, double>' requested here
double f = check_for_type<double>(t2);
^
t.cpp:58:16: note: 'check_for_type' should be declared prior to the call site
constexpr bool check_for_type(tuple<> t) {
^
1 error generated.
What's wrong with this piece of code?
Upvotes: 1
Views: 1755
Reputation: 12928
Since you are using c++17 in your code, I thought it would make sense to point out that there are a lot of new tools to avoid having to make these kind of recursive templates.
You could condense the whole thing to this:
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T1, typename... T2>
constexpr bool check_for_type(std::tuple<T2...>) {
return std::disjunction_v<std::is_same<T1, T2>...>;
}
int main() {
std::tuple<int, char, bool> tup;
std::cout << check_for_type<char>(tup) << '\n';
std::cout << check_for_type<float>(tup) << std::endl;
return 0;
}
If std::disjunction
gets no parameters it defaults to false, so passing an empty tuple is also covered here.
Upvotes: 7
Reputation: 25297
template<class X, class F, class... R> constexpr bool check_for_type(tuple<F,R...> t) { if constexpr(is_same<F,X>::value) { return true; } return check_for_type<X>(tail(t)); }
You are calling check_for_type<X>(...)
before it is declared. Helpfully, your error message states:
t.cpp:58:16: note: 'check_for_type' should be declared prior to the call site constexpr bool check_for_type(tuple<> t) {
Once you do that, the code compiles:
// Put this function first
template<class X>
constexpr bool check_for_type(tuple<> t) {
return false;
}
template<class X, class F, class... R>
constexpr bool check_for_type(tuple<F,R...> t) {
if constexpr(is_same<F,X>::value) {
return true;
}
// Right here, the compiler looks up `check_for_type` and doesn't find
// an overload that can be called with just one explicit type parameter.
return check_for_type<X>(tail(t));
}
Upvotes: 2