Reputation: 16935
I read this question and thought it was interesting, so I started playing with some code to see if I could get it to work, but I ran into an issue.
My approach is to use the head-tail idiom familiar to functional programming. However, I could not find a way to deal with an empty variadic template list, which would be the base case.
Here's my code:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class C {};
class D : public C {};
/*
// Forward declaration
template <typename T1, typename T2, typename... Args>
struct are_convertible;
*/
// There are no Args
template <>
struct are_convertible<> {
static const bool value = true;
};
// Check if the first two elements are convertible then recurse on the rest
template <typename T1, typename T2, typename... Args>
struct are_convertible {
static const bool value = std::is_convertible<T1, T2>::value && are_convertible<Args...>::value;
};
int main() {
std::cout << std::boolalpha;
std::cout << "Are convertible A->B and C->D: " << are_convertible<A, B, C, D>::value << std::endl; // Should be false
}
I'm currently getting an error that states 'are_convertible' is not a class template
, so I tried to forward declare it, which gave this error:
error: wrong number of template arguments (0, should be at least 2)
How can I fix my approach?
Upvotes: 4
Views: 1575
Reputation: 9997
You have two problems.
First, with your forward declaration, you say that your template always accepts at least two arguments (T1
and T2
). If you want to allow no parameters for your struct, you need to forward-declare it with only variadic parameter:
template<typename... Args>
struct are_convertible;
Second, your second definition is not partial specialization, but a full general (anew) template definition that contradicts the previous forward declaration. What you need is partial specialization:
template <typename T1, typename T2, typename... Args>
struct are_convertible<T1, T2, Args...> {
//^^^^^^^^^^^^^^^^^
After this, your code works:
class A {};
class B : public A {};
class C {};
class D : public C {};
template<typename... Args>
struct are_convertible;
// There are no Args
template <>
struct are_convertible<> {
static const bool value = true;
};
// Check if the first two elements are convertible then recurse on the rest
template <typename T1, typename T2, typename... Args>
struct are_convertible<T1, T2, Args...> {
static const bool value = std::is_convertible<T1, T2>::value && are_convertible<Args...>::value;
};
int main() {
std::cout << std::boolalpha;
std::cout << "Are convertible A->B and C->D: " << are_convertible<A, B, C, D>::value << std::endl;
std::cout << "Are convertible B->A and D->C: " << are_convertible<B, A, D, C>::value << std::endl;
}
This prints false
and true
, which seems to be a correct result for me.
Upvotes: 5
Reputation: 56557
Your specialization is wrong. T1
and T2
disappeared. Partially specialize for T1
and T2
, which implies no other parameters are left for Args...
. Code below:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class C {};
class D : public C {};
// Forward declaration
template <typename T1, typename T2, typename... Args>
struct are_convertible;
// There are no Args
template <typename T1, typename T2>
struct are_convertible<T1, T2> {
static const bool value = std::is_convertible<T1, T2>::value;
};
// Check if the first two elements are convertible then recurse on the rest
template <typename T1, typename T2, typename... Args>
struct are_convertible {
static const bool value = std::is_convertible<T1, T2>::value && are_convertible<Args...>::value;
};
int main() {
std::cout << std::boolalpha;
std::cout << "Are convertible A->B and C->D: " << are_convertible<A, B, C, D>::value << std::endl; // Should be false
}
Upvotes: 1