【问题标题】:Check if a class explicitely defines a member type in an inheritance hierarchy检查一个类是否明确定义了继承层次结构中的成员类型
【发布时间】:2018-06-16 15:04:05
【问题描述】:

我有一组使用成员 typedef Next 链接的类,如下所示:

class Y; class Z;
class X { public: typedef Y Next; };
class Y { public: typedef Z Next; };
class Z { };

我需要一种方法来获取链的最终类,从链的任何类开始。感谢accepted answer of this post,我写了如下代码:

// cond_type<Condition, Then, Else>::type   // selects type 'Then' if 'Condition' is true, or type 'Else' otherwise
template <bool Condition, typename Then, typename Else = void>
struct cond_type
{
    typedef Then type;
};
template <typename Then, typename Else>
struct cond_type<false, Then, Else >
{
    typedef Else type;
};

template <class C, typename _ = void> 
struct chain
{
    typedef C last; 
};
template <class C>
struct chain<C, typename cond_type<false, typename C::Next>::type>
{
    typedef typename chain<typename C::Next>::last last;
};

使用上面的模板chain&lt;C&gt;::last,下面的代码按预期正确地实例化了类Z的3个对象:

chain<X>::last z1;
chain<Y>::last z2;
chain<Z>::last z3;

但是,如果所考虑的类集形成继承层次结构,则采用以下方式:

class U; class V;
class T             { public: typedef U Next; };
class U : public T  { public: typedef V Next; };
class V : public U  { };

然后,使用模板chain&lt;C&gt;::last,与上述集合的任何类C,例如:

chain<T>::last v;

导致以下编译错误:

1>test.cpp(88): error C3646: 'last' : unknown override specifier

我知道问题是类V继承自父类U中定义的typedef V Next,导致编译模板chain&lt;V,V&gt;的特殊形式,而应该使用通用形式代替V 没有成员 Next

无论如何,我被困在这里,因为我需要一种有效的机制,即使在这种类层次结构的情况下也是如此。

我该怎么做?

PS:类之间的继承必须保持公开;成员 typedef 必须保持公开。

【问题讨论】:

  • 如果您使用的是 c++11 或最新版本,则不必定义您的 cond_type :请参阅 fr.cppreference.com/w/cpp/types/conditional
  • 你应该考虑将继承层次和链抽象分开,顺便说一下:这样会简化实现并且更容易维护,即使它需要更多的类。

标签: c++ c++11 templates c++14 sfinae


【解决方案1】:

很简单:

template <typename T, typename = void> struct last_t_impl
{
    using type = T;
};
template <typename T> struct last_t_impl
    <T, std::enable_if_t<!std::is_same_v<typename T::Next, T>>>
{
    using type = typename last_t_impl<typename T::Next>::type;
};
template <typename T> using last_t = typename last_t_impl<T>::type;

用法:

last_t<T> v1;
last_t<U> v2;
last_t<V> v3;

如果您需要为 C++14(而不是 C++17)编译上述代码,请将 std::is_same_v&lt;A,B&gt; 更改为 std::is_same&lt;A,B&gt;::value


请注意,您的 typename cond_type&lt;false, T&gt;::type 可以替换为 std::void_t&lt;T&gt;(或 C++14 中的 std::conditional_t&lt;false,T,void&gt;)。但在这种情况下,不需要它,因为链的末端将由std::is_same_v&lt;typename T::Next, T&gt; 检测到 SFINAE。 (即使 T::Next 因某种原因不存在,SFINAE 仍会启动,last_t&lt;T&gt; 将只是 T。)

【讨论】:

  • 我正在寻找使用 std::is_same 的东西,但找不到。您的解决方案效果很好,比我的要简洁得多。非常感谢。
【解决方案2】:

类型链类应该作为一个独立于类层次结构的抽象来实现。

#include <iostream>
#include <string>
#include <type_traits>

// Definitions for TypeChain as an abstraction

// Simple interface to declare next class for a class.
template <typename N>
struct Next {
    using next = N;
};

// Primary template for a type chain.
template <typename T>
struct TypeChain : Next<void>{};


/// Implementation of type-function last.
template <typename T, typename C = typename TypeChain<T>::next> struct last_t_impl
{
    using type = std::conditional_t<std::is_same_v<C, void>, T, typename last_t_impl<C>::type>;
};

// Never used, but needed to end recursion.
template <> struct last_t_impl<void, void>
{
    using type = void;
};

template <typename T> using last_t = typename last_t_impl<T>::type;

// Définition of the class hierarchy, without chaining.
class T             { };
class U : public T  { };
class V : public U  { };

// Definition of the chain.
// T => U => V

// Specialisation of TypeChain for T
template<>
struct TypeChain<T> : Next<U> {};

// Specialisation of TypeChain for U
template<> 
struct TypeChain<U> : Next<V>{};

// No specialisation for V, since it has no next value.


// Test (should run three 1)
int main()
{
    std::cout << std::is_same_v<last_t<T>,  V> << "\n";
    std::cout << std::is_same_v<last_t<U>,  V> << "\n";
    std::cout << std::is_same_v<last_t<V>,  V> << "\n";
}

它无法检测到链循环,但此实现将两个抽象(类层次结构和类型链)保持为独立的概念,因此您可以在不修改类层次结构的情况下提供这些功能,这在我的查看。

【讨论】:

  • 我提供的示例是对真实代码的简化。我实际上需要使类型链在每个类中都明确可见。无论如何,感谢您的建议,它可能对其他用途有用。
猜你喜欢
  • 1970-01-01
  • 2013-04-30
  • 2020-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
  • 2013-11-08
  • 1970-01-01
相关资源
最近更新 更多