【问题标题】:Template argument after scope resolution operator is not substituted范围解析运算符后的模板参数未被替换
【发布时间】:2015-09-06 09:17:37
【问题描述】:

我正在尝试制作一个通用代码,如果 B 不是 D 的祖先,则会导致编译错误。我想出了什么:

template<typename B, typename D>
struct assert_base_of {
    enum {value = sizeof(B::D)};
}

它不起作用。当我这样“称呼”它时:

assert_base_of<A2,A1>::value;

我收到以下错误(g++ 4.8.2):

main.cpp:16:22: error: ‘D’ is not a member of ‘A2’

看起来模板参数 D 没有被 A1 替换。谁能解释一下并提出解决方案?

【问题讨论】:

  • static_assert (std::is_base_of&lt;B, D&gt;::value, "B must be a base of D") 有什么问题?
  • 我不允许使用 std::is_base_of,即使可以,我仍然想知道为什么 D 没有被替换 :)
  • B::D 被解释为成员(不是类型),因为B 在这里是依赖的。无论如何,你不能得到这样的基类——你需要将一个指针从D 转换为B 以检查D 是否继承B。有关如何实施is_base_of,请参阅@Veritas 答案。
  • Bdependent 表示它依赖于一个模板参数(这里它一个模板参数)。当您编写B::foo 时,如果B 是依赖的,则foo 始终被解释为B 成员的名称(这是因为编译器在您的模板上查看了两次,并且在第一遍中它不知道B 最终会是什么)。如果你想让foo 命名一个类型,你必须使用typename B::foo。如果要foo 命名模板,则必须使用template B::foo
  • 另外,您在非模板sizeof(A::B) 案例中观察到的是将B 的注入类名注入B(typenameB 被“注入”到B,而A 继承了它——A::A 也作为一种类型存在)。这是您应该避免的 C++ 的一个黑暗角落,因为它与模板的行为不同。请参阅stackoverflow.com/questions/7025054/… 和相关问题。这里重要的是A::B 命名一个类型,而不是一个成员。

标签: c++ templates scope-resolution


【解决方案1】:

继承不会将派生类包含在基类的范围内,因此为此使用范围解析运算符是没有意义的。正确的替代方法(也适用于多重继承)是滥用重载解决规则:

#include <iostream>
#include <type_traits>

template<typename Base, typename Derived,
       bool = std::is_same<Base, Derived>::value>
struct is_base_of_impl
{

  typedef typename std::remove_cv<Base>::type     no_cv_base;      
  typedef typename std::remove_cv<Derived>::type  no_cv_derived;


  template<typename _Up>
  static std::true_type test(no_cv_derived&, _Up);
  static std::false_type test(no_cv_base&, int);

  //Black Magic
  struct converter
  {
   operator no_cv_derived&();
   operator no_cv_base&() const;
  };

  static const bool value = decltype(test(converter(), 0))::value;
};

template<typename Base, typename Derived>
struct is_base_of_impl<Base, Derived, true>
{ 
    static const bool value = std::is_same<Base, Derived>::value; 
};

template<typename Base, typename Derived>
struct is_base_of
: public std::integral_constant<bool,
               is_base_of_impl<Base, Derived>::value>
{ };


struct A {};
struct B1 : A {};
struct B2 : A {};
struct C : B1, B2 {};

int main()
{
    std::cout << is_base_of<A, B1>::value << "\n";
    std::cout << is_base_of<B1, C>::value << "\n";
    std::cout << is_base_of<A, C>::value << "\n";
    return 0;
}

有关更多信息,请查看以下链接:

How does `is_base_of` work?

https://groups.google.com/d/msg/comp.lang.c++.moderated/xv4VlXq2omE/--WAroYkW2QJ

【讨论】:

  • 我可能错了,但经过测试,我认为您的解决方案不适用于多重继承 - 如果 A 从 B1 和 B2 继承,并且两者都从 C 继承(非虚拟继承),我当我做 is_base_of::value. 时得到 0
  • @pyx:不过,您可以使用is_base_of&lt;C,B1&gt;::value &amp;&amp; is_base_of&lt;B1, A&gt;::value。我想这应该更恰当地称为is_direct_base_of
  • @Veritas:欢迎来到 1k rep!
  • @AlexandreC。我只是在编辑答案,这也是 libstdc++ 内部使用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
  • 2018-05-14
  • 2015-11-15
  • 2012-02-14
  • 2010-09-09
  • 2015-12-18
相关资源
最近更新 更多