如何检查功能模板是否专业?

发布于 2025-01-31 12:05:59 字数 224 浏览 3 评论 0 原文

如果某个功能模板是专门的,是否有办法在编译时间建立?

例如,假设以下功能模板:

template<size_t N>
void foo();

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

我尝试了Sfinae,但找不到编译器无法从声明中推断出的功能的条件。

Is there a way to establish at compile time if a certain function template was specialized?

For example, assume the following function template:

template<size_t N>
void foo();

I want to test if foo<42> was specialized. Note that the declaration above doesn't contain any default implementation.

I tried SFINAE but couldn't find a condition on the function that the compiler cannot deduce from its declaration.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陌若浮生 2025-02-07 12:05:59

如果某个模板函数专业化,是否可以在编译时间内建立?

有一个功能...我不这么认为。

但是,如果创建函数,则可以添加一个静态const成员( 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); }

这需要更多的工作,但是可以用不同的函数( im_not_specialized 基于类型)重复使用Isspecialized()

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

#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
 }

Is there a way to establish in compile time if a certain template function was specialized?

With a function... I don't think so.

But if you create a functor, you can add a static const member (is_specialized, in the following example) that can give you this information

#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
 }

--- EDIT ---

Following the suggestion from Quentin (thanks again!) I've developed another functor-based solution that use something, to detect if the functor is generic or specialize, that is added only in the generic functor. In this case, a type instead a bool constant.

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; }
 };

This type can be used via SFINAE and I propose an example based on a constexpr isSpecialized() template function (with an helper function)

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); }

This require a little more work but isSpecialized() can be reused with different functors (im_not_specialized type based)

The following is a full working example

#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
 }
甜妞爱困 2025-02-07 12:05:59

如果将基本函数标记为已删除( = delete ),则可以检测使用Sfinae是否已专业(假设专业化本身未删除),

例如 exptype(foo&lt; n&gt) ;(())如果 foo&lt; n&gt; 标记为已删除,则会导致替换失败。如果您提供了未删除的专业,则表达式不会导致错误。

使用此功能您可以创建一个简单的特质类,以检查 foo 是否专门用于一组模板参数:

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

1。基本示例

C ++ 11: godbolt

#include <type_traits>

template<std::size_t N>
void foo() = delete;

template<>
void foo<1>() { }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

int main()
{
    static_assert(!is_foo_specialized<0>::value, ""); // foo<0> is not specialized
    static_assert(is_foo_specialized<1>::value, ""); // foo<1> IS specialized
}

使用C ++ 20,您也可以为此使用一个概念,例如: godbolt

#include <type_traits>

template<std::size_t N>
void foo() = delete;

template<>
void foo<1>() { }

template<std::size_t N>
concept is_foo_specialized = requires { foo<N>(); };

int main()
{
    static_assert(!is_foo_specialized<0>); // foo<0> is not specialized
    static_assert(is_foo_specialized<1>); // foo<1> IS specialized
}

2。由于函数为 = delete 'd提供默认实现

,因此无法具有默认实现。

如果确实需要该函数的默认实现,则可以使用2个函数:

  • 一个是 = delete 'd(因此,Sfinae可以检测到它)
  • ,另一个实现默认行为并将其转发至另一个如果存在专业化,则

C ++ 11: godbolt

#include <type_traits>
#include <iostream>

template<std::size_t N>
void foo_specialized() = delete;

template<>
void foo_specialized<1>() { std::cout << "CUSTOMIZED!" << std::endl; }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo_specialized<N>(), void())> : std::true_type {};

template<std::size_t N>
typename std::enable_if<!is_foo_specialized<N>::value>::type foo() {
    std::cout << "DEFAULT!" << std::endl;
}

template<std::size_t N>
typename std::enable_if<is_foo_specialized<N>::value>::type foo() {
    foo_specialized<N>();
}

int main()
{
    foo<0>(); // -> DEFAULT!
    foo<1>(); // -> CUSTOMIZED!
}

或c ++ 20: godbolt

#include <type_traits>
#include <iostream>

template<std::size_t N>
void foo_specialize() = delete;

template<>
void foo_specialize<1>() { std::cout << "CUSTOMIZED!" << std::endl; }

template<std::size_t N>
concept is_foo_specialized = requires { foo_specialize<N>(); };


template<std::size_t N> requires (!is_foo_specialized<N>)
void foo() {
    std::cout << "DEFAULT!" << std::endl;
}

template<std::size_t N> requires (is_foo_specialized<N>)
void foo() {
    foo_specialize<N>();
}

int main()
{
    foo<0>(); // -> DEFAULT!
    foo<1>(); // -> CUSTOMIZED!
}

3。编译时代的恶作剧,

这当然也可以用来迭代专业化(在一定限制内) -或您在评论中要求找到该功能最近的专业化。

最近的_foo_specialized 在此示例中将迭代 n 的一系列值,并检查是否存在此值的 foo 的专业化。

  • n 是我们要启动搜索的值
  • searchrange 确定从提供的 n 中检查了多少个专业(均可向上和向下)值(在此示例中,我们检查 n 's +/- 10)
  • currentDistance 保持跟踪我们已经搜索过的距离 n 值,因此我们不会超过指定的 searchrange
  • 最后一个模板参数用于sfinae

,例如:

最近的_foo_specialized&lt; 100,100,10&gt; 将检查的专业知识foo n = 90 n = 110 ,返回接近100的示例)

C ++ 11: godbolt

#include <type_traits>
#include <iostream>
#include <utility>

template<std::size_t N>
void foo() = delete;

template<>
void foo<5>() { std::cout << 5 << std::endl; }

template<>
void foo<10>() { std::cout << 10 << std::endl; }

template<>
void foo<15>() { std::cout << 15 << std::endl; }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

template<std::size_t N, std::size_t SearchRange = 10, std::size_t CurrentDistance = 0, class = void>
struct nearest_foo_specialized {
    static const std::size_t index = 0; // an index for which foo<> is specialized, if value is true.
    static const std::size_t distance = CurrentDistance; // distance from original N
    static const bool value = false; // have we found a specialization yet?
};

// Found a match -> Report Success
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance <= SearchRange && is_foo_specialized<N>::value >::type> {
    static const std::size_t index = N;
    static const std::size_t distance = CurrentDistance;
    static const bool value = true;
};

// No match found -> recurse until SearchRange limit
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance < SearchRange && !is_foo_specialized<N>::value >::type> {
    typedef nearest_foo_specialized<N - 1, SearchRange, CurrentDistance + 1> down;
    typedef nearest_foo_specialized<N + 1, SearchRange, CurrentDistance + 1> up;

    static const std::size_t distance = down::distance < up::distance ? down::distance : up::distance;
    static const std::size_t index = down::distance == distance && down::value ? down::index : up::index;
    static const std::size_t value = down::distance == distance && down::value ? down::value : up::value;
};

// calls the nearest foo() specialization (up to 10 away from the specified N)
template<std::size_t N>
typename std::enable_if<nearest_foo_specialized<N>::value>::type call_nearest_foo() {
    foo<nearest_foo_specialized<N>::index>();
}

template<std::size_t N>
typename std::enable_if<!nearest_foo_specialized<N>::value>::type call_nearest_foo() {
    static_assert(N!=N, "No nearest foo() specialization found!");
}


int main() {
    call_nearest_foo<7>(); // calls foo<5>()
    call_nearest_foo<8>(); // calls foo<10>()

    call_nearest_foo<11>(); // calls foo<10>()
    call_nearest_foo<15>(); // calls foo<15>()

    call_nearest_foo<25>(); // calls foo<15>()
    // call_nearest_foo<26>(); // error: No nearest foo() (only searching up to 10 up / down)
}

If you mark the base function as deleted (= delete), you can detect if it has been specialized using SFINAE (assuming the specialization itself is not deleted)

An expression like decltype(foo<N>()) will result in a substitution failure if foo<N> is marked as deleted. If you provide a specialization that is not deleted on the other hand the expression will not result in an error.

Using this you can create a simple trait class to check if foo has been specialized for a specific set of template parameters:

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

1. Basic examples

C++11: godbolt

#include <type_traits>

template<std::size_t N>
void foo() = delete;

template<>
void foo<1>() { }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

int main()
{
    static_assert(!is_foo_specialized<0>::value, ""); // foo<0> is not specialized
    static_assert(is_foo_specialized<1>::value, ""); // foo<1> IS specialized
}

With C++20 you could also use a concept for this, e.g.: godbolt

#include <type_traits>

template<std::size_t N>
void foo() = delete;

template<>
void foo<1>() { }

template<std::size_t N>
concept is_foo_specialized = requires { foo<N>(); };

int main()
{
    static_assert(!is_foo_specialized<0>); // foo<0> is not specialized
    static_assert(is_foo_specialized<1>); // foo<1> IS specialized
}

2. Providing a default implementation

Due to the function being = delete'd it can't have a default implementation.

If you do require a default implementation for the function, you could use 2 functions instead:

  • one that is = delete'd (so SFINAE can detect it)
  • and another one that implements the default behaviour and forwards to the other if a specialization exists

C++11: godbolt

#include <type_traits>
#include <iostream>

template<std::size_t N>
void foo_specialized() = delete;

template<>
void foo_specialized<1>() { std::cout << "CUSTOMIZED!" << std::endl; }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo_specialized<N>(), void())> : std::true_type {};

template<std::size_t N>
typename std::enable_if<!is_foo_specialized<N>::value>::type foo() {
    std::cout << "DEFAULT!" << std::endl;
}

template<std::size_t N>
typename std::enable_if<is_foo_specialized<N>::value>::type foo() {
    foo_specialized<N>();
}

int main()
{
    foo<0>(); // -> DEFAULT!
    foo<1>(); // -> CUSTOMIZED!
}

Or with C++20: godbolt

#include <type_traits>
#include <iostream>

template<std::size_t N>
void foo_specialize() = delete;

template<>
void foo_specialize<1>() { std::cout << "CUSTOMIZED!" << std::endl; }

template<std::size_t N>
concept is_foo_specialized = requires { foo_specialize<N>(); };


template<std::size_t N> requires (!is_foo_specialized<N>)
void foo() {
    std::cout << "DEFAULT!" << std::endl;
}

template<std::size_t N> requires (is_foo_specialized<N>)
void foo() {
    foo_specialize<N>();
}

int main()
{
    foo<0>(); // -> DEFAULT!
    foo<1>(); // -> CUSTOMIZED!
}

3. Compile-time shenanigans

This can of course also be used to iterate the specializations (within a certain limit) - or like you asked in the comments to find the nearest specialization of the function.

nearest_foo_specialized in this example will iterate over a range of values for N and check if a specialization of foo exists for this value.

  • N is the value where we want to start the search
  • SearchRange determines how many specializations will be checked (both up and down) from the provided N value (in this example we check for N's +/- 10)
  • CurrentDistance keeps track how far we've already searched from our starting N value, so we don't exceed the specified SearchRange
  • The last template parameter is used for SFINAE

e.g.:

nearest_foo_specialized<100, 10> would check for specializations of foo between N = 90 and N = 110, returning the one that is closer to 100 (prefering lower N values in case of a draw)

Example C++11: godbolt

#include <type_traits>
#include <iostream>
#include <utility>

template<std::size_t N>
void foo() = delete;

template<>
void foo<5>() { std::cout << 5 << std::endl; }

template<>
void foo<10>() { std::cout << 10 << std::endl; }

template<>
void foo<15>() { std::cout << 15 << std::endl; }

template<std::size_t N, class = void>
struct is_foo_specialized : std::false_type {};

template<std::size_t N>
struct is_foo_specialized<N, decltype(foo<N>(), void())> : std::true_type {};

template<std::size_t N, std::size_t SearchRange = 10, std::size_t CurrentDistance = 0, class = void>
struct nearest_foo_specialized {
    static const std::size_t index = 0; // an index for which foo<> is specialized, if value is true.
    static const std::size_t distance = CurrentDistance; // distance from original N
    static const bool value = false; // have we found a specialization yet?
};

// Found a match -> Report Success
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance <= SearchRange && is_foo_specialized<N>::value >::type> {
    static const std::size_t index = N;
    static const std::size_t distance = CurrentDistance;
    static const bool value = true;
};

// No match found -> recurse until SearchRange limit
template<std::size_t N, std::size_t SearchRange, std::size_t CurrentDistance>
struct nearest_foo_specialized<N, SearchRange, CurrentDistance, typename std::enable_if< CurrentDistance < SearchRange && !is_foo_specialized<N>::value >::type> {
    typedef nearest_foo_specialized<N - 1, SearchRange, CurrentDistance + 1> down;
    typedef nearest_foo_specialized<N + 1, SearchRange, CurrentDistance + 1> up;

    static const std::size_t distance = down::distance < up::distance ? down::distance : up::distance;
    static const std::size_t index = down::distance == distance && down::value ? down::index : up::index;
    static const std::size_t value = down::distance == distance && down::value ? down::value : up::value;
};

// calls the nearest foo() specialization (up to 10 away from the specified N)
template<std::size_t N>
typename std::enable_if<nearest_foo_specialized<N>::value>::type call_nearest_foo() {
    foo<nearest_foo_specialized<N>::index>();
}

template<std::size_t N>
typename std::enable_if<!nearest_foo_specialized<N>::value>::type call_nearest_foo() {
    static_assert(N!=N, "No nearest foo() specialization found!");
}


int main() {
    call_nearest_foo<7>(); // calls foo<5>()
    call_nearest_foo<8>(); // calls foo<10>()

    call_nearest_foo<11>(); // calls foo<10>()
    call_nearest_foo<15>(); // calls foo<15>()

    call_nearest_foo<25>(); // calls foo<15>()
    // call_nearest_foo<26>(); // error: No nearest foo() (only searching up to 10 up / down)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文