【问题标题】:Detecting basic_string instantiation检测 basic_string 实例化
【发布时间】:2011-07-02 18:09:20
【问题描述】:

我编写了以下代码来确定一个类型是否是std::basic_string 的实例化:

template <typename T>
struct is_string
{
    enum { value = false };
};

template <typename charT, typename traits, typename Alloc>
struct is_string<std::basic_string<charT, traits, Alloc> >
{
    enum { value = true };
};

有没有更简洁的方法来实现这一点?

【问题讨论】:

  • 这是否正确检测到basic_string 的第二个和第三个模板参数与默认值不同的实例?
  • @Steve 好点,希望已修复,请参阅我的编辑。
  • 非常好。我喜欢第二种解决方案。

标签: c++ string templates metaprogramming template-meta-programming


【解决方案1】:

使用一点 SFINAE 甚至可以检查该类型是否源自某个基本字符串:

#include <type_traits>

namespace detail
{
    template <typename T, class Enable = void>
    struct is_string : std::false_type {};

    template <typename... T>
    struct void_helper { typedef void type; };

    template <typename T>
    struct is_string<T,
            typename void_helper<
                        typename T::value_type,
                        typename T::traits_type,
                        typename T::allocator_type
                        >::type
                >
        : std::is_base_of<
                            std::basic_string<
                                typename T::value_type,
                                typename T::traits_type,
                                typename T::allocator_type
                            >,
                            T
                         >
    {};
}

template <typename T>
struct is_string : detail::is_string<T> {};

【讨论】:

  • 现在是不是“更简洁”了?哈哈。无论如何,我认为代码中存在错误。如果派生类声明了value_type 或其他名称之一,它将隐藏basic_string 的名称,而is_string 将命中派生类的名称。
  • 实际上,如果你重新定义 value_type、traits_type 或 allocator_type 你真的没有任何理由让 is_string 为真。而且我并没有声称它更简洁,只是它还处理派生类:)
【解决方案2】:

我承认的另一种方法不像您的解决方案那么简洁:-),但是也检测从 basic_string 派生的类和指向字符串的指针的方法是:

namespace string_traits
{
    typedef char yes_type;

    struct no_type
    {
        char padding[8];
    };


    no_type string_helper(...);

    template <typename charT, typename traits, typename Alloc>
    yes_type string_helper(std::basic_string<charT, traits, Alloc>);

    template <typename T> 
    typename std::remove_pointer<T>::type MakeT();

    template <typename T>
    struct is_string : std::integral_constant<bool,sizeof(string_helper(MakeT<T>()))==sizeof(yes_type)> {};

    template <> struct is_string<void> : std::false_type { };
}

class TestString : public std::basic_string<char> { };
class A { };

template <bool b>
void check()
{
    cout << (b?"Is string":"Is not string") << endl;
}

int main()
{
    using namespace string_traits;

    //not strings
    check<is_string<void>::value>();
    check<is_string<int>::value>();
    check<is_string<int(&)[2]>::value>();
    check<is_string<string(&)[2]>::value>();
    check<is_string<string**>::value>();
    check<is_string<A>::value>();

    //strings
    check<is_string<string>::value>();
    check<is_string<TestString>::value>();
    check<is_string<TestString&>::value>();
    check<is_string<TestString*>::value>();
    check<is_string<std::wstring>::value>();
    check<is_string<string*>::value>();
    check<is_string<string&>::value>();
}

【讨论】:

    【解决方案3】:

    好的,我找到了一个稍微短一点的方法:

    #include <type_traits>
    
    template <typename T>
    struct is_string : std::false_type {};
    
    template <typename charT, typename traits, typename Alloc>
    struct is_string<std::basic_string<charT, traits, Alloc> > : std::true_type {};
    

    但也许其他人可以做得更好? :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多