为容器中的不同字符串类型实现编译时 "static-if" 逻辑
Implementing a compile-time "static-if" logic for different string types in a container
我想编写一个对字符串容器进行操作的函数模板,例如 std::vector
。
我想用相同的模板函数同时支持 CString
和 std::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);
}
}
这是一个语法漂亮的。
目标是摆脱@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>
的别名)。
我想编写一个对字符串容器进行操作的函数模板,例如 std::vector
。
我想用相同的模板函数同时支持 CString
和 std::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);
}
}
这是一个语法漂亮的。
目标是摆脱@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>
的别名)。