用于从 ANSI 字符串转换为 std::basic_string<TCHAR> 的正确函数声明
A proper function declaration for conversion from ANSI string to std::basic_string<TCHAR>
我试图通过以下方式声明一个将 const char * 转换为 std::basic_string 的函数:
#include <string>
#include <cstring>
#ifdef _MSC_VER
#include <tchar.h>
#else
#define TCHAR char
#endif
typedef TCHAR Char;
typedef std::basic_string<Char> String;
template <typename = typename std::enable_if< !std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
String dest(std::strlen(p_src), ' ');
auto p_cur = p_src;
for (auto & ch : dest)
{
ch = *p_cur++;
}
return dest;
}
template <typename = typename std::enable_if< std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
return p_src;
}
但遇到编译器错误。我的想法是有两个重载并根据 TCHAR 类型启用其中一个。执行此操作的正确方法是什么?
VC2017的错误是:
error C2995: 'String FromACString(const char *)': function template has already been defined
您的代码有两个问题:
- SFINAE 仅适用于依赖上下文。由于
typename std::enable_if<!std::is_same<Char, char>::value>::type
中的任何内容都不依赖于任何模板参数,因此 SFINAE 不适用,并且由于 std::enable_if<false>::type
无效,您会遇到硬错误。
- 在确定模板的"uniqueness"时,只考虑参数类型本身,而不考虑它们的默认值。您有两个函数模板定义
template <typename> String FromACString(const char*)
,这是一个错误。
要解决第一个问题,可以引入另一个模板参数,默认值为Char
。通常解决第二个问题的方法是使用 enable_if
表达式本身作为模板参数类型,而不是默认值。这给你留下了这样的东西:
template <typename C = Char,
std::enable_if_t<!std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
//...
}
template <typename C = Char,
std::enable_if_t<std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
//...
}
现在,根据 Char
的定义方式,您最终会得到一个带有签名
的模板
template <typename, void*>
String FromACString(const char*);
还有一个格式不正确,因此被 SFINAE 禁用。
Miles 回答了您的 enable_if
出了什么问题。但是,您可以使用 if constexpr
:
来避免 enable_if
的细微差别
inline std::basic_string<TCHAR>
FromACString(const char * p_src)
{
if constexpr (std::is_same_v<TCHAR, char>) {
// logic for case where TCHAR is char
} else {
// logic for case where TCHAR isn't char
}
}
PS。您将 TCHAR 转换为 wchar_t
(我认为这是 char
之外唯一可能的类型)应该考虑字符编码。您可以使用 mbsrtowcs.
我试图通过以下方式声明一个将 const char * 转换为 std::basic_string 的函数:
#include <string>
#include <cstring>
#ifdef _MSC_VER
#include <tchar.h>
#else
#define TCHAR char
#endif
typedef TCHAR Char;
typedef std::basic_string<Char> String;
template <typename = typename std::enable_if< !std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
String dest(std::strlen(p_src), ' ');
auto p_cur = p_src;
for (auto & ch : dest)
{
ch = *p_cur++;
}
return dest;
}
template <typename = typename std::enable_if< std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
return p_src;
}
但遇到编译器错误。我的想法是有两个重载并根据 TCHAR 类型启用其中一个。执行此操作的正确方法是什么?
VC2017的错误是:
error C2995: 'String FromACString(const char *)': function template has already been defined
您的代码有两个问题:
- SFINAE 仅适用于依赖上下文。由于
typename std::enable_if<!std::is_same<Char, char>::value>::type
中的任何内容都不依赖于任何模板参数,因此 SFINAE 不适用,并且由于std::enable_if<false>::type
无效,您会遇到硬错误。 - 在确定模板的"uniqueness"时,只考虑参数类型本身,而不考虑它们的默认值。您有两个函数模板定义
template <typename> String FromACString(const char*)
,这是一个错误。
要解决第一个问题,可以引入另一个模板参数,默认值为Char
。通常解决第二个问题的方法是使用 enable_if
表达式本身作为模板参数类型,而不是默认值。这给你留下了这样的东西:
template <typename C = Char,
std::enable_if_t<!std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
//...
}
template <typename C = Char,
std::enable_if_t<std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
//...
}
现在,根据 Char
的定义方式,您最终会得到一个带有签名
template <typename, void*>
String FromACString(const char*);
还有一个格式不正确,因此被 SFINAE 禁用。
Miles 回答了您的 enable_if
出了什么问题。但是,您可以使用 if constexpr
:
enable_if
的细微差别
inline std::basic_string<TCHAR>
FromACString(const char * p_src)
{
if constexpr (std::is_same_v<TCHAR, char>) {
// logic for case where TCHAR is char
} else {
// logic for case where TCHAR isn't char
}
}
PS。您将 TCHAR 转换为 wchar_t
(我认为这是 char
之外唯一可能的类型)应该考虑字符编码。您可以使用 mbsrtowcs.