There are tons of questions about the least common ancestor algorithm, but this one is different because I'm trying to determine the LCA at compile-time, and my tree is neither binary nor a search tree, even though my simplified version may look like one.
Suppose you have a bunch of structures which contain a member typedef parent
, which is another similar structure:
struct G
{
typedef G parent; // 'root' node has itself as parent
};
struct F
{
typedef G parent;
};
struct E
{
typedef G parent;
};
struct D
{
typedef F parent;
};
struct C
{
typedef F parent;
};
struct B
{
typedef E parent;
};
struct A
{
typedef E parent;
};
which together make a tree such as
A B C D
\ / \ /
E F
\ /
\ /
\ /
G
NOTE: There is no inheritance relationship between the structures.
What I would like to do is create a type-trait least_common_ancestor
such that:
least_common_ancestor<A, B>::type; // E
least_common_ancestor<C, D>::type; // F
least_common_ancestor<A, E>::type; // E
least_common_ancestor<A, F>::type; // G
What's the best way to do this?
I'm not concerned with algorithmic complexity particularly since the tree depth is small, rather I'm looking for the simplest meta-program that that will get to the correct answer.
EDIT: I need to be able to build the solution with msvc2013, among other compilers, so answers without constexpr
are preferred.
This may probably be improved, but you could first compute the depth of your type and then use this information to go up one branch or the other:
template <typename U, typename = typename U::parent>
struct depth {
static const int value = depth<typename U::parent>::value + 1;
};
template <typename U>
struct depth<U, U> {
static const int value = 0;
};
The above will basically compute the depth of your type in your tree.
Then you could use std::enable_if
:
template <typename U, typename V, typename Enabler = void>
struct least_common_ancestor;
template <typename U>
struct least_common_ancestor<U, U> {
using type = U;
};
template <typename U, typename V>
struct least_common_ancestor<U, V,
typename std::enable_if<(depth<U>::value < depth<V>::value)>::type> {
using type = typename least_common_ancestor<U, typename V::parent>::type;
};
template <typename U, typename V>
struct least_common_ancestor<U, V,
typename std::enable_if<(depth<V>::value < depth<U>::value)>::type> {
using type = typename least_common_ancestor<V, typename U::parent>::type;
};
template <typename U, typename V>
struct least_common_ancestor<U, V,
typename std::enable_if<!std::is_same<U, V>::value && (depth<V>::value == depth<U>::value)>::type> {
using type = typename least_common_ancestor<typename V::parent, typename U::parent>::type;
};
Output:
int main(int, char *[]) {
std::cout << std::is_same<least_common_ancestor<A, B>::type, E>::value << std::endl;
std::cout << std::is_same<least_common_ancestor<C, D>::type, F>::value << std::endl;
std::cout << std::is_same<least_common_ancestor<A, E>::type, E>::value << std::endl;
std::cout << std::is_same<least_common_ancestor<A, F>::type, G>::value << std::endl;
std::cout << std::is_same<least_common_ancestor<A, A>::type, A>::value << std::endl;
return 0;
}
Gives:
1 1 1 1 1
This may probably be improved but could be used as a starting point.