const_cast, кажется, игнорируется шаблонами C++?

Я написал простой класс ведения журнала, который поддерживает шаблоны с переменным числом символов в C++ с использованием Visual Studio в Windows. Я создал общий 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;
        }
    }
};

Код отлично работает с константными значениями. Однако, если я введу неконстантные параметры, например, так:

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 не решает проблему).

Итак, чтобы попробовать лучшее из обоих миров, я попытался добавить следующее условие:

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 "

Тем не менее, я получил переполнение стека. Для отладки я добавил 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, но не было никакой разницы в результате.

Почему актерский состав не работает? Как мне решить это, пожалуйста?

2 ответа

Решение

Если First является wchar_t* актерский состав создаст wchar_t* constне const wchar_t*, Приведение не является текстовой заменой имен типов.

Таким образом, вы создаете новый тип, который не соответствует какой-либо специализации, и получаете рекурсивный вызов универсального Log функция.

const_cast используется только для приведения типа от константного к неконстантному. И это работает, только если указанная переменная не была первоначально объявлена ​​как const.

Вам не нужно приводить что-то к const, вы можете просто скопировать это в ссылку на const или значение const этого типа

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

Эта функция вернет const char* без кастинга. Это также может быть сделано с назначением.

Другие вопросы по тегам