const_cast 似乎被 C++ 模板忽略了?
const_cast seems to be ignored with C++ templates?
我在 Windows 上使用 Visual Studio 编写了一个简单的日志记录 class,它支持 C++ 中的可变参数模板。我创建了一个通用的 Log
函数模板,其中包含许多专门化的内容,以满足可能输入的常见组合。
#pragma once
#include <Windows.h>
#include <locale>
#include <codecvt>
#include <string>
#include <sstream>
#include <utility>
using namespace std;
inline static string to_utf8(const wstring& s) {
wstring_convert<codecvt_utf8_utf16<wchar_t>> utf16conv;
return utf16conv.to_bytes(s);
}
class Logger {
public:
HANDLE file_handle;
wchar_t file_path[MAX_PATH + 1];
inline Logger(HANDLE handle) : file_handle(handle), file_path{} {
}
inline Logger(const string& path) : Logger(path.c_str()) {
}
inline Logger(const wstring& path) : Logger(path.c_str()) {
}
inline Logger(const char* path) : file_handle(NULL) {
wstring_convert<codecvt_utf8_utf16<wchar_t>> converter;
wcscpy_s(file_path, MAX_PATH + 1, converter.from_bytes(path).c_str());
}
inline Logger(const wchar_t* path) : file_handle(NULL) {
wcscpy_s(file_path, MAX_PATH + 1, path);
}
private:
inline void VerifyInitialize() {
if ((file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) && file_path[0] != '[=11=]') {
file_handle = CreateFileW(file_path, FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(file_path, 0, NULL, FILE_END);
}
}
public:
inline void Log() {
}
template<typename ...Rest>
inline void Log(const char* first, Rest... params) {
VerifyInitialize();
if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)
return;
DWORD written;
WriteFile(file_handle, first, static_cast<DWORD>(strlen(first)), &written, NULL);
Log(params...);
}
template<typename ...Rest>
inline void Log(const char first, Rest... params) {
char str[2];
str[0] = first;
str[1] = '[=11=]';
Log(str, params...);
}
template<typename ...Rest>
inline void Log(const string* first, Rest... params) {
VerifyInitialize();
if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)
return;
DWORD written;
WriteFile(file_handle, first->c_str(), static_cast<DWORD>(first->size()), &written, NULL);
Log(params...);
}
template<typename ...Rest>
inline void Log(const string& first, Rest... params) {
Log(&first, params...);
}
template<typename ...Rest>
inline void Log(const wstring* first, Rest... params) {
Log(*first, params...);
}
template<typename ...Rest>
inline void Log(const wstring& first, Rest... params) {
Log(to_utf8(first), params...);
}
template<typename ...Rest>
inline void Log(const wchar_t* first, Rest... params) {
Log(wstring(first), params...);
}
template<typename ...Rest>
inline void Log(const wchar_t first, Rest... params) {
wchar_t str[2];
str[0] = first;
str[1] = '[=11=]';
Log(str, params...);
}
template<typename First, typename ...Rest>
inline void Log(First first, Rest... params) {
printf("%s\n", typeid(First).name());
if (is_const<First>::value) {
stringstream stream;
stream << first;
Log(stream.str(), params...);
} else
Log(const_cast<const First>(first), params...);
}
inline ~Logger() {
if (!(file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)) {
CloseHandle(file_handle);
file_handle = NULL;
}
}
};
该代码适用于 const 值。但是,如果我像这样引入非常量参数:
int main() {
Logger logger(("output.txt"));
wchar_t sometext[3];
sometext[0] = '1';
sometext[1] = '8';
sometext[2] = '[=12=]';
logger.Log(sometext, L"\n");
return 0;
}
不调用特化,而是调用最后一个泛型 void Log(First first, Rest... params)
,它使用 stringstream
并将指针写为字符串而不是字符串本身。
如果我从所有重载参数中删除 const
,当我调用 main()
时它会起作用,但如果我用 const char*
替换 sometext
,那么最后一个泛型void Log(First first, Rest... params)
被调用而不是特化(即删除 const
不能解决问题)。
因此,为了尝试两全其美,我尝试添加以下条件:
template<typename First, typename ...Rest>
inline void Log(First first, Rest... params) {
if (is_const<First>::value) {
stringstream stream;
stream << first;
Log(stream.str(), params...);
} else
Log(const_cast<const First>(first), params...);
}
其基本原理是指示编译器 "first search for a const specialization overload, and if none was found, then fallback to using stringstream
".
但是,我遇到了堆栈溢出。为了调试,我在 if
条件之前添加了 printf("%s\n", typeid(First).name());
,输出为:
wchar_t * __ptr64
wchar_t * __ptr64
wchar_t * __ptr64
wchar_t * __ptr64
....
显然,const_cast<const First>(first)
似乎实际上并没有转换为 const
,尽管 is_const::value
确实返回了 false
。我也尝试过使用std::as_const
,但结果没有什么不同。
为什么转换不起作用?请问我该如何解决?
const_cast 仅用于将类型从 const 转换为非常量。这仅在所述变量最初未声明为 const 时有效。
您不需要强制转换 某些内容为 const,您只需将其复制到该类型的 const 引用或 const 值即可
const char* changeSecond(char* string, char change)
{
string[0] = change;
return string;
}
该函数将 return 变成 const char*
,无需强制转换。这也可以通过赋值来完成。
如果 First
是 wchar_t*
,演员将创建 wchar_t* const
,而不是 const wchar_t*
。转换不是类型名称的文本替换。
因此您创建了一个不匹配任何特化的新类型,并获得对通用 Log
函数的递归调用。
我在 Windows 上使用 Visual Studio 编写了一个简单的日志记录 class,它支持 C++ 中的可变参数模板。我创建了一个通用的 Log
函数模板,其中包含许多专门化的内容,以满足可能输入的常见组合。
#pragma once
#include <Windows.h>
#include <locale>
#include <codecvt>
#include <string>
#include <sstream>
#include <utility>
using namespace std;
inline static string to_utf8(const wstring& s) {
wstring_convert<codecvt_utf8_utf16<wchar_t>> utf16conv;
return utf16conv.to_bytes(s);
}
class Logger {
public:
HANDLE file_handle;
wchar_t file_path[MAX_PATH + 1];
inline Logger(HANDLE handle) : file_handle(handle), file_path{} {
}
inline Logger(const string& path) : Logger(path.c_str()) {
}
inline Logger(const wstring& path) : Logger(path.c_str()) {
}
inline Logger(const char* path) : file_handle(NULL) {
wstring_convert<codecvt_utf8_utf16<wchar_t>> converter;
wcscpy_s(file_path, MAX_PATH + 1, converter.from_bytes(path).c_str());
}
inline Logger(const wchar_t* path) : file_handle(NULL) {
wcscpy_s(file_path, MAX_PATH + 1, path);
}
private:
inline void VerifyInitialize() {
if ((file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) && file_path[0] != '[=11=]') {
file_handle = CreateFileW(file_path, FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(file_path, 0, NULL, FILE_END);
}
}
public:
inline void Log() {
}
template<typename ...Rest>
inline void Log(const char* first, Rest... params) {
VerifyInitialize();
if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)
return;
DWORD written;
WriteFile(file_handle, first, static_cast<DWORD>(strlen(first)), &written, NULL);
Log(params...);
}
template<typename ...Rest>
inline void Log(const char first, Rest... params) {
char str[2];
str[0] = first;
str[1] = '[=11=]';
Log(str, params...);
}
template<typename ...Rest>
inline void Log(const string* first, Rest... params) {
VerifyInitialize();
if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)
return;
DWORD written;
WriteFile(file_handle, first->c_str(), static_cast<DWORD>(first->size()), &written, NULL);
Log(params...);
}
template<typename ...Rest>
inline void Log(const string& first, Rest... params) {
Log(&first, params...);
}
template<typename ...Rest>
inline void Log(const wstring* first, Rest... params) {
Log(*first, params...);
}
template<typename ...Rest>
inline void Log(const wstring& first, Rest... params) {
Log(to_utf8(first), params...);
}
template<typename ...Rest>
inline void Log(const wchar_t* first, Rest... params) {
Log(wstring(first), params...);
}
template<typename ...Rest>
inline void Log(const wchar_t first, Rest... params) {
wchar_t str[2];
str[0] = first;
str[1] = '[=11=]';
Log(str, params...);
}
template<typename First, typename ...Rest>
inline void Log(First first, Rest... params) {
printf("%s\n", typeid(First).name());
if (is_const<First>::value) {
stringstream stream;
stream << first;
Log(stream.str(), params...);
} else
Log(const_cast<const First>(first), params...);
}
inline ~Logger() {
if (!(file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)) {
CloseHandle(file_handle);
file_handle = NULL;
}
}
};
该代码适用于 const 值。但是,如果我像这样引入非常量参数:
int main() {
Logger logger(("output.txt"));
wchar_t sometext[3];
sometext[0] = '1';
sometext[1] = '8';
sometext[2] = '[=12=]';
logger.Log(sometext, L"\n");
return 0;
}
不调用特化,而是调用最后一个泛型 void Log(First first, Rest... params)
,它使用 stringstream
并将指针写为字符串而不是字符串本身。
如果我从所有重载参数中删除 const
,当我调用 main()
时它会起作用,但如果我用 const char*
替换 sometext
,那么最后一个泛型void Log(First first, Rest... params)
被调用而不是特化(即删除 const
不能解决问题)。
因此,为了尝试两全其美,我尝试添加以下条件:
template<typename First, typename ...Rest>
inline void Log(First first, Rest... params) {
if (is_const<First>::value) {
stringstream stream;
stream << first;
Log(stream.str(), params...);
} else
Log(const_cast<const First>(first), params...);
}
其基本原理是指示编译器 "first search for a const specialization overload, and if none was found, then fallback to using stringstream
".
但是,我遇到了堆栈溢出。为了调试,我在 if
条件之前添加了 printf("%s\n", typeid(First).name());
,输出为:
wchar_t * __ptr64
wchar_t * __ptr64
wchar_t * __ptr64
wchar_t * __ptr64
....
显然,const_cast<const First>(first)
似乎实际上并没有转换为 const
,尽管 is_const::value
确实返回了 false
。我也尝试过使用std::as_const
,但结果没有什么不同。
为什么转换不起作用?请问我该如何解决?
const_cast 仅用于将类型从 const 转换为非常量。这仅在所述变量最初未声明为 const 时有效。
您不需要强制转换 某些内容为 const,您只需将其复制到该类型的 const 引用或 const 值即可
const char* changeSecond(char* string, char change)
{
string[0] = change;
return string;
}
该函数将 return 变成 const char*
,无需强制转换。这也可以通过赋值来完成。
如果 First
是 wchar_t*
,演员将创建 wchar_t* const
,而不是 const wchar_t*
。转换不是类型名称的文本替换。
因此您创建了一个不匹配任何特化的新类型,并获得对通用 Log
函数的递归调用。