【问题标题】:How to check if a template function was specialized?如何检查模板功能是否专门化?
【发布时间】:2017-03-28 14:46:05
【问题描述】:

如果某个模板函数是专门化的,有没有办法在编译时建立?

例如,假设以下函数:

template<size_t N>
void foo();

我想测试foo&lt;42&gt; 是否是专业的。请注意,上面的声明不包含任何默认实现。

我尝试了 SFINAE,但找不到编译器无法从其声明中推断出的函数条件。

有什么想法吗?

谢谢

【问题讨论】:

  • 嗯,你会在哪里以及为什么要使用这样的东西?也许对您最初的问题有更优雅的解决方案。
  • 好吧,我不能使用重载和 SFINAE,因为我有一个非类型模板参数。但是,我可以在函数中添加一个未命名的 std::integral_constant&lt;size_t, 42&gt;。但我希望那里可能有一个更清洁的解决方案......
  • 非类型模板参数不会阻止您使用重载或 SFINAE。目前还不清楚您为什么要检测到这一点。
  • 不清楚这里的实际问题是什么。
  • 假设我正在开发一个基于标头的库,它允许用户根据自己的喜好多次专门化foo,然后使用某个size_t 模板调用库中的模板函数参数,并且此函数将委托给使用最接近的数字专门化的函数。这可能吗?

标签: c++ c++11 templates template-meta-programming template-specialization


【解决方案1】:

如果某个模板函数是专门的,有没有办法在编译时建立?

有了函数……我不这么认为。

但是如果你创建了一个仿函数,你可以添加一个静态常量成员(is_specialized,在下面的例子中),它可以给你这个信息

#include <iostream>

template <std::size_t N>
struct foo
 {
   static constexpr bool is_specialized { false };

   void operator() () const
    { std::cout << "- generic (" << N << ") foo struct" << std::endl; }
 };

template <>
struct foo<42U>
 {
   static constexpr bool is_specialized { true };

   void operator() () const
    { std::cout << "- specialized (42) foo struct" << std::endl; }
 };

int main()
 {
   foo<17U>()(); // print - generic (17) foo struct
   foo<42U>()(); // print - specialized (42) foo struct

   std::cout << foo<17U>::is_specialized << std::endl; // print 0
   std::cout << foo<42U>::is_specialized << std::endl; // print 1
 }

--- 编辑---

根据 Quentin 的建议(再次感谢!)我开发了另一种基于仿函数的解决方案,它使用某些东西来检测仿函数是通用的还是专门的,它只添加在通用仿函数中。在这种情况下,使用类型而不是 bool 常量。

template <std::size_t N>
struct foo
 {
   // im_not_specialized is added only in the generic version!
   using im_not_specialized = void;

   void operator () () const
    { std::cout << "- generic (" << N << ") foo struct" << std::endl; }
 };

template <>
struct foo<42U>
 {
   void operator () () const
    { std::cout << "- specialized (42) foo struct" << std::endl; }
 };

这种类型可以通过 SFINAE 使用,我提出了一个基于 constexpr isSpecialized() 模板函数的示例(带有辅助函数)

template <typename F>
constexpr bool isSpecializedHelper
      (int, typename F::im_not_specialized const * = nullptr)
 { return false; }

template <typename F>
constexpr bool isSpecializedHelper (long)
 { return true; }

template <typename F>
constexpr bool isSpecialized ()
 { return isSpecializedHelper<F>(0); }

这需要更多的工作,但 isSpecialized() 可以与不同的仿函数重用(基于 im_not_specialized 类型)

以下是一个完整的工作示例

#include <iostream>

template <std::size_t N>
struct foo
 {
   // im_not_specialized is added only in the generic version!
   using im_not_specialized = void;

   void operator () () const
    { std::cout << "- generic (" << N << ") foo struct" << std::endl; }
 };

template <>
struct foo<42U>
 {
   void operator () () const
    { std::cout << "- specialized (42) foo struct" << std::endl; }
 };

template <typename F>
constexpr bool isSpecializedHelper
      (int, typename F::im_not_specialized const * = nullptr)
 { return false; }

template <typename F>
constexpr bool isSpecializedHelper (long)
 { return true; }

template <typename F>
constexpr bool isSpecialized ()
 { return isSpecializedHelper<F>(0); }

int main()
 {
   foo<17U>()(); // print - generic (17) foo struct
   foo<42U>()(); // print - specialized (42) foo struct

   constexpr auto isSp17 = isSpecialized<foo<17U>>();
   constexpr auto isSp42 = isSpecialized<foo<42U>>();

   std::cout << isSp17 << std::endl; // print 0
   std::cout << isSp42 << std::endl; // print 1
 }

【讨论】:

  • 更进一步:在基本模板中声明一个虚拟的using im_not_specialized = void;,然后再检测它。现在用户不必做任何额外的事情了:)
  • @Quentin - 不确定是否理解... im_not_specialized 应该添加到is_specialized 或用来替代它?什么时候应该使用im_not_specialized
  • 它取代了is_specialized。然后您可以使用 SFINAE 来检测您正在查看的 foo&lt;?&gt; 是否包含 typedef(然后它来自基本模板)(然后它已经被专门化了)。
  • 嗯,它仍然取决于使用函子。我正在寻找保留函数模板的解决方案。
  • @Quentin - 我期待看到您基于模板的解决方案;我在 SFINAE 方面不是很好,但是,如果您不担心,我会尝试在我的回答中添加一个基于您的 im_not_specialized 的示例