如何限制模板中的类型名称

How to restrict typenames in template

本文关键字:类型 何限制      更新时间:2023-10-16

有一个可以接受4种不同类型的函数。它们的实现非常相似。

template< typename T >
void foo( std::string &&name, T value ){
    //...
}

typename T必须是 4 种预定义类型之一。其他类型名是不可接受的,应在编译时加以限制。
可以用C++写吗?

我至少可以想到三种方法,在我的头顶上。

第一:

namespace internal {
template< typename T >
void foo_impl( std::string &&name, T value ) {
    // actual implementation here
}
}
void foo(std::string &&name, SpecialType1 value ) {
    internal::foo_impl(std::forward<std::string>(name), value);
}
void foo(std::string &&name, SpecialType2 value ) {
    internal::foo_impl(std::forward<std::string>(name), value);
}

第二:

template< typename T >
typename std::enable_if<std::is_same<T, SpecialType1>::value ||
                        std::is_same<T, SpecialType2>::value >::type
foo( std::string &&name, T value ){
    //implementation here
}

第三:

template< typename T >
void foo(std::string &&name, T value){
    static_assert(std::is_same<T, SpecialType1>::value ||
                  std::is_same<T, SpecialType2>::value,
    "wrong template argument");
    //implementation here
}

演示

<type_traits>允许您将逻辑概括为类模板。这是如何工作的是我们获取模板参数TTs的参数包,然后开始检查T是否与Head类型列表相同。如果找到匹配项,我们从std::true_type继承,我们就完成了。如果没有找到匹配项,我们弹出头部并递归实例化带有 Ts 尾部的相同模板。最终,如果根本找不到匹配项,参数包大小将降至零,编译器将实例化从 std::false_type 继承的基本模板类。请观看此视频,了解沃尔特·E·布朗先生的更好和部门解释。

template<class T, class...> struct is_any_of: std::false_type{};
template<class T, class Head, class... Tail> 
struct is_any_of<T, Head, Tail...>: std::conditional_t<
  std::is_same<T, Head>::value, 
  std::true_type,
  is_any_of<T, Tail...>>
{};

现在我们可以用几乎英文的措辞使用enable_if。

#include <type_traits>
#include <string>
template<
  class T,
  class = std::enable_if_t<is_any_of<T, int, float, unsigned, double>::value>
>
void foo(std::string &&str, T value) {}

int main()
{
  foo(std::string{"hello"}, 3);
  foo(std::string{"world"}, '0'); //compile-time error
}

SFANIE是一种语言功能,一种工具,它被用来或滥用一些人来实现你的要求,

标准库组件 std::enable_if 允许创建替换失败,以便根据编译时评估的条件启用或禁用特定的重载。 仅供参考 http://en.cppreference.com/w/cpp/language/sfinae。

请注意,std::conditional_t<>std::enable_if_t<> 分别是 std::conditional<>::typestd::enable_if<>::type 的简写。您可以在代码中简单地替换它们,但应该在enable_if之前放置typename关键字。

我见过人们做的一件事是使用 std::enable_if .我不知道你的 4 种类型到底是什么,所以这是一个有两种类型的例子 intfloat .

using myType = std::enable_if<
std::is_same<int, T>::value ||
std::is_same<float, T>::value, T >::type;

只有当T正好intfloat时,myType才存在。希望对您有所帮助!

我知道

我参加聚会迟到了,但也许有人会发现这很有用。

C++20起,可以使用约束和概念来过滤模板中可能的typename

语法是这样的:

template <typename T>
requires std::is_integral_v<T>
auto func(T any_integer) {
  ...
};
// Or, you can also do:
template <typename T>
concept Integer = std::is_integral_v<T>;
template <Integer T>
auto func(T any_integer) {
  ...
};
// Or, an even better approach:
auto func(Integer auto any_integer) {
  ...
};

我更喜欢最后一个,因为它很好,很干净。但是,这只是我的意见。

以下是对func()的一些有效调用:

func(int{});         // OK!
func(long{});        // OK!
func(bool{});        // Yes, it's OK too!
func(char{});        // Yes, it's OK too!

现在,一些对func()的无效调用:

func(double{});      // Compiler error!
func(std::string{}); // Compiler error!

编译器还会根据您的使用情况给出干净的错误消息:

注意:候选:"模板需要is_integral_v无效功能(T(">

注意:候选:"模板需要整数自动函数(T(">

注意:候选:"模板需要整数自动:15 自动功能(自动:15(">

引用:

  1. https://en.cppreference.com/w/cpp/types/is_integral