为容器中的不同字符串类型实现编译时 "static-if" 逻辑

Implementing a compile-time "static-if" logic for different string types in a container

我想编写一个对字符串容器进行操作的函数模板,例如 std::vector

我想用相同的模板函数同时支持 CStringstd::wstring

问题是CString和wstring有不同的接口,比如要得到一个CString的"length",你调用GetLength()方法,而不是wstring 你叫 size()length().

如果我们在 C++ 中有一个 "static if" 特性,我可以这样写:

template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
    for (const auto & s : strings)
    {
        static_if(strings::value_type is CString) 
        {
            // Use the CString interface
        }
        static_else_if(strings::value_type is wstring)
        {   
            // Use the wstring interface
        }
    }
}

是否有一些模板编程技术可以使用当前可用的 C++11/14 工具实现此目标?

PS
我知道可以用 vector<CString>vector<wstring> 编写几个 DoSomething() 重载,但这不是问题的重点。
此外,我希望此函数模板适用于您可以使用 range-for 循环对其进行迭代的任何容器。

您可以提供两个重载来获取长度:

template<typename T>
std::size_t getLength(T const &str)
{
    return str.size();
}

std::size_t getLength(CString const &str)
{
  return str.GetLength();
}

您可以提供满足您需要的函数重载:

size_t getSize(const std::string& str)
{
    return str.size();
}

size_t getSize(const CString& str)
{
    return str.GetLength();
}

template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
    for (const auto & s : strings)
    {
        ...
        auto size = getSize(s);
        ...
    }
}

解决这个问题的一种常见方法是将所需的接口提取到特征中 class。像这样:

template <class S>
struct StringTraits
{
  static size_t size(const S &s) { return s.size(); }
  // More functions here
};


template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
    for (const auto & s : strings)
    {
      auto len = StringTraits<typename std::decay<decltype(s)>::type>::size(s);
    }
}


// Anyone can add their own specialisation of the traits, such as:

template <>
struct StringTraits<CString>
{
  static size_t size(const CString &s) { return s.GetLength(); }
  // More functions here
};

当然,您可以花点时间更改函数本身,以允许在基于类型的选择之外进行特征选择:

template <class ContainerOfStrings, class Traits = StringTraits<typename ContainerOfString::value_type>>
void DoSomething(const ContainerOfStrings& strings)
#include <type_traits>

template <typename T, typename F>
auto static_if(std::true_type, T t, F f) { return t; }

template <typename T, typename F>
auto static_if(std::false_type, T t, F f) { return f; }

template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(std::integral_constant<bool, B>{}, t, f); }

template <bool B, typename T>
auto static_if(T t) { return static_if(std::integral_constant<bool, B>{}, t, [](auto&&...){}); }

测试:

template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
    for (const auto & s : strings)
    {
        static_if<std::is_same<typename ContainerOfStrings::value_type, CString>{}>
        ([&](auto& ss)
        {
            // Use the CString interface
            ss.GetLength();
        })(s);

        static_if<std::is_same<typename ContainerOfStrings::value_type, wstring>{}>
        ([&](auto& ss)
        {
            // Use the wstring interface
            ss.size();
        })(s);
    }
}

DEMO

这是一个语法漂亮的。

目标是摆脱@Piotr 解决方案中多余的 ()

大量样板文件:

template<bool b>
struct static_if_t {};
template<bool b>
struct static_else_if_t {};

struct static_unsolved_t {};

template<class Op>
struct static_solved_t {
  Op value;
  template<class...Ts>
  constexpr
  decltype(auto) operator()(Ts&&...ts) {
    return value(std::forward<Ts>(ts)...);
  }
  template<class Rhs>
  constexpr
  static_solved_t operator->*(Rhs&&)&&{
    return std::move(*this);
  }
};
template<class F>
constexpr
static_solved_t<std::decay_t<F>> static_solved(F&& f) {
  return {std::forward<F>(f)};
}

template<class F>
constexpr
auto operator->*(static_if_t<true>, F&& f) {
  return static_solved(std::forward<F>(f));
}
template<class F>
constexpr
static_unsolved_t operator->*(static_if_t<false>, F&&) {
  return {};
}
constexpr
static_if_t<true> operator->*(static_unsolved_t, static_else_if_t<true>) {
  return {};
}
constexpr
static_unsolved_t operator->*(static_unsolved_t, static_else_if_t<false>) {
  return {};
}

template<bool b>
constexpr static_if_t<b> static_if{};

template<bool b>
constexpr static_else_if_t<b> static_else_if{};

constexpr static_else_if_t<true> static_else{};

这是使用时的样子:

template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings) {
  for (const auto & s : strings)
  {
    auto op = 
    static_if<std::is_same<typename ContainerOfStrings::value_type,CString>{}>->*
    [&](auto&& s){
        // Use the CString interface
    }
    ->*static_else_if<std::is_same<typename ContainerOfStrings::value_type, std::cstring>{}>->*
    [&](auto&& s){   
        // Use the wstring interface
    };
    op(s); // fails to compile if both of the above tests fail
  }
}

支持 static_else_if 的无限链。

它不妨碍你无限链static_else(上面的static_else只是static_else_if<true>的别名)。