使用static_assert来确定特定模板参数是否为特定的非类型化类模板

Using a static_assert to determine if a specific template parameter is a specific untyped class template

本文关键字:类型化 是否 参数 assert 使用 static      更新时间:2023-10-16

我想要一个函数,将参数限制为仅从特定模板类派生的类型。在这种情况下,basic_string(来自STL文档)。例如,声明一个wstring

typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >
wstring;

基本想法是这样的:

template <class TString>
void strings_only_please(TString message) {
    static_assert(is_base_of<basic_string, TString>::value, 
        "Not a string type!");  
}

当然,这不会编译,尽管还没有指定basic_string。。。它需要一个真正的类型。(虽然我可能只是对少数实际的字符串类型进行硬编码,但我正在寻找这种模式的通用解决方案。)

我使用的是Visual Studio 2012,理想情况下希望代码能够移植到其他现代C++编译器,如GCC。

有三种解决问题的方法,一种是实现is_specialization_of,另一种是使函数采用std::basic_string<T1,T2,T3>而不是TString,第三种方法与第二种解决方案具有相同的原理;使模板仅可由CCD_ 6匹配。


is_base_of在您的示例中是不够的,原因有两个:

  1. is_base_of用于查看类型U是否派生自T(或者是否为同一类型),在您的代码段中不涉及继承。

  2. std::basic_string不是一个完整的类型,因此根本不能与is_base_of一起使用(您已经指出了这一点)。


解决方案#1

is_specialization_of将用于检查类型U是否是不完整类型T的专门化。使用模板模板类很容易实现它,如下例所示。

正如@SebastianRedl所指出的,使用VS2012无法获得可变模板,请参阅其他解决方案(这些解决方案不是通用的,但仍然足以满足您的需求)

#include <type_traits>
#include <iostream>
#include <string>
template<template<typename...> class T, typename U>
struct is_specialization_of              : std::false_type { };
template<template<typename...> class T, typename... Ts> 
struct is_specialization_of<T, T<Ts...>> : std::true_type  { };
int
main (int argc, char *argv[])
{
  std::cerr << is_specialization_of<std::basic_string, std::string >::value << std::endl;
  std::cerr << is_specialization_of<std::basic_string, std::wstring>::value << std::endl;
  std::cerr << is_specialization_of<std::basic_string, std::istream>::value << std::endl;
}

输出

1
1
0

解决方案#2

template <typename T1, typename T2, typename T3>
void strings_only_please(std::basic_string<T1,T2,T3>) {
  // ...  
}

当然,以上不会导致很好的static_assert错误,但它足以满足您的需求,并满足您的要求;该函数只能由专门化CCD_ 17的类型调用。


解决方案#3

template<typename T>
struct is_basic_string : std::false_type { };
template<typename T1, typename T2, typename T3>
struct is_basic_string<std::basic_string<T1,T2,T3>> : std::true_type { };
...
is_basic_string<std::string >::value // true
is_basic_string<std::istream>::value // false