【问题标题】:Technique to distinguish between ordinary template parameters and template template parameters using SFINAE resolution使用 SFINAE 分辨率区分普通模板参数和模板模板参数的技术
【发布时间】:2014-09-17 03:10:49
【问题描述】:

这个问题的灵感来自最近出现的question about extended std::is_base_of type trait

有什么技术可以让我们在现代 C++ 或其扩展中区分普通模板参数和模板模板参数(例如,-std=gnu++1zclang++/g++)?

namespace details
{

template< /* ??? */ base >
struct is_derived_from;

template< typaneme base >
struct is_derived_from< base >
{
    static std::true_type test(base *);
    static std::false_type test(void *);
};

template< template< typename ...formal > base >
struct is_derived_from< /* ??? */ >
{
    template< typename ...actual > // actual parameters must be here!
    static std::true_type test(base< actual... > *);
    static std::false_type test(void *);
};

} // namespace details

template< typename derived, /* ??? */ base >
using is_derived_from = decltype(details::is_derived_from< /* ? base< ? > */ >::test(std::declval< typename std::remove_cv< derived >::type * >()));

在积极的情况下,它允许我们使一些有用的类型特征更加强大(例如,STL 的std::is_base_of)。

我认为它需要一种语言特性作为“通用类型名”,不是吗?

【问题讨论】:

  • 你不能重载类模板。不过,我认为您可以使用重载 constexpr 函数模板。
  • @T.C.它们之间是否存在我们可以利用的根本差异?
  • @T.C.您不需要constexpr,只需在decltype 内部进行重载解析,无需评估。
  • @Potatoswatter 但是如果您希望能够同时执行foo&lt;Template, Derived&gt;foo&lt;BaseClass, Derived&gt; 并在编译时使用结果,那么您需要使用一对constexpr 函数,不?
  • @T.C.只要存在重载决议,您就可以将constexpr int foo() { return 1; } 替换为std::integral_constant&lt; int, 1 &gt; foo(); // no definition。不要判断哪个更好,您只是不需要需要 constexpr :P 。不过后者可能更便携。

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


【解决方案1】:

类模板只能有一组模板参数,但您可以使用重载constexpr 函数模板来代替分派到适当的类模板。采用linked question 中的is_derived_from 特征,并带有一个额外的SFINAE 参数,这样当B 是一个不可访问或模棱两可的基时,您就不会遇到硬错误:

#include <type_traits>
namespace detail
{
    template <template <class...> class B, typename Derived>
    struct is_derived_from
    {
        using U = typename std::remove_cv<Derived>::type;

        template <typename... Args, 
                  typename = std::enable_if_t<
                             std::is_convertible<U*, Base<Args...>*>::value>>
        static auto test(B<Args...>*)
            -> typename std::integral_constant<bool
                                           , !std::is_same<U, B<Args...>>::value>;

        static std::false_type test(void*);

        using type = decltype(test(std::declval<U*>()));
    };

    using std::is_base_of; // may want to use is_convertible instead to match
                           // the semantics of is_derived_from
}

template <template <class...> class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; }

template <class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; }

struct B {};
struct D : B {};

template<class ...>
struct B2 {}; 
struct D2 : B2<int, double> { };

int main() {
  static_assert(my_is_base_of<B2, D2>(), "Oops");
  static_assert(my_is_base_of<B, D>(), "Oops");
  static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops");
  static_assert(!my_is_base_of<B, D2>(), "Oops");
}

Demo.

【讨论】:

  • struct BB : B&lt; struct X &gt;, B&lt; struct Y &gt; {}; 不会导致 clang++ 出现硬错误。
  • @Orient 这很有趣——但不是我所说的模棱两可的基数。
  • 可能是因为new memory model.
  • @Orient 不,这是clang bug 16068(和18834)。 (内存模型与什么有什么关系?)
  • 也许这不是错误:也许参数应该被分割到匹配的基类的第一次出现?
【解决方案2】:

你问:

有什么技术可以让我们区分普通模板参数和现代 C++ 或其扩展中的模板模板参数(例如,-std=gnu++1zclang++/g++)?

在我看来你需要类似的东西:

template <typename T>
struct is_template_template : public std::false_type
{
};

template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};

示例程序

#include <iostream>

template <typename T>
struct is_template_template : public std::false_type
{
};

template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};

template <typename T> struct A {};
struct B {};

int main()
{
   std::cout << std::boolalpha;
   std::cout << is_template_template<A<int>>::value << std::endl;
   std::cout << is_template_template<B>::value << std::endl;
   return 0;
}

输出:

真的 错误的

【讨论】:

  • 你误解了我的问题,但我是这种情况的罪魁祸首(由于我的英语知识不佳)。
  • 请举例说明如何在现实生活中使用您的构造。类型特征应该能够识别提供的模板参数的一些有意义的非平凡属性。
  • 实参不能是template template。我们无法提供is_template_template&lt; A &gt;::value,即无法检查标识符是否为模板类的名称。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
  • 2018-01-29
  • 2019-01-15
  • 2019-11-22
相关资源
最近更新 更多