使用C ++模板时,const_cast似乎被忽略了吗?

时间:2017-10-04 22:53:21

标签: c++ c++11 templates const const-cast

我编写了一个简单的日志记录类,它在Windows上使用Visual Studio支持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] != '\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并将指针写为字符串而不是字符串本身。

如果我从所有重载参数中删除const,当我调用main()时它会起作用,但如果我用sometext替换const char*,那么最后一个void Log(First first, Rest... params) const调用1}}而不是专门化(即删除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...); } 并不能解决问题)。

因此,为了尝试两全其美,我尝试添加以下条件:

stringstream

理由是指示编译器“首先搜索const专门化重载,如果没有找到,则回退到使用printf("%s\n", typeid(First).name());

但是,我得到了堆栈溢出。为了调试,我在if条件之前添加了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。我也尝试使用{{1}},但结果没有差异。

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

2 个答案:

答案 0 :(得分:1)

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

你不需要强制转换某些东西到const,你只需将它复制到const引用或该类型的const值

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

该函数将返回const char*,无需进行强制转换。这也可以通过分配来完成。

答案 1 :(得分:1)

如果Firstwchar_t*,演员表将创建wchar_t* const,而不是const wchar_t*。强制转换不是类型名称的文本替换。

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