使用static_assert确定特定模板参数是否是特定的无类型类模板

时间:2013-07-10 15:07:00

标签: c++ c++11

我希望有一个函数将参数限制为仅来自特定模板化类的类型。在这种情况下,basic_string(来自STL - docs)。例如,声明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。

1 个答案:

答案 0 :(得分:10)

有三种方法可以解决您的问题,一种是is_specialization_of的实现,另一种是让您的函数采用std::basic_string<T1,T2,T3>代替TString,第三种方法是与第二种解决方案相同的理念;使模板仅由std::basic_string匹配。


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

  1. is_base_of用于查看类型U是否来自T(或者如果它是相同的类型),在您的代码段中不涉及继承。

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


  3. 解决方案#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错误 - 但它足以满足您的需求并做您想做的事情;该函数只能由专门化std::basic_string的类型调用。


    解决方案#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