const_cast似乎被C++模板忽略了?

问题描述:

我写了一个简单的日志记录类,在Windows上使用Visual Studio在C++中支持variadic模板。我创建了一个通用的Log函数模板,其中包含许多专业知识,以满足可能输入的通用组合。const_cast似乎被C++模板忽略了?

#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] != '\0') { 
      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] = '\0'; 
     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] = '\0'; 
     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值。然而,如果我介绍非const参数,像这样:

int main() { 
    Logger logger(("output.txt")); 
    wchar_t sometext[3]; 
    sometext[0] = '1'; 
    sometext[1] = '8'; 
    sometext[2] = '\0'; 
    logger.Log(sometext, L"\n"); 
    return 0; 
} 

的专业化不叫,而是最后的通用void Log(First first, Rest... params)被调用,它使用stringstream并写入指针作为一个字符串,而不是字符串本身。

如果我从所有的过载参数,当调用main()它的工作原理,但如果我有const char*替换sometext,那么最后的通用void Log(First first, Rest... params)被调用,而不是特例(即除去const没有解决不了的问题,请删除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...); 
} 

与理由是编译器指令,以“先寻找一个const专业化超载,如果没有找到,则回退到使用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,但结果没有差异。

为什么演员不工作?我该如何解决这个问题?

如果Firstwchar_t*演员阵营将创建一个wchar_t* const而不是const wchar_t*。该转换不是类型名称的文本替换。

因此,您创建一个不匹配任何专业化的新类型,并获得对通用Log函数的递归调用。

+0

除了“const to pointer”专精之外,我还为每种类型添加了“指向const”的特化,并且一切正常!万分感谢:)) –

const_cast仅用于将类型从const转换为非const。而且这只有在所述变量最初不是被声明为const时才有效。

你不需要东西为const,你可以将它复制到该类型

const char* changeSecond(char* string, char change) 
{ 
    string[0] = change; 
    return string; 
} 

的引用或常量,用来函数会返回一个const char*,而无需进行转换需要。这也可以通过分配完成。