Home > Enterprise >  Failure deducing non-type template argument because of char-array to pointer decay?
Failure deducing non-type template argument because of char-array to pointer decay?

Time:10-23

Given the following code:

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <source_location>

template<std::size_t N>
struct StringWrapper {
    // consteval to force only compile-time strings
    consteval StringWrapper(const char (&format_string)[N], std::source_location source_location = std::source_location::current())
        : SourceInfo{ source_location } {
        std::copy(std::begin(format_string), std::end(format_string), std::begin(FormatString));
    }

    char FormatString[N]{};

    // This here to motivate not using StringWrapper as non-type template argument
    std::source_location SourceInfo{};
};

// class-template-argument-deduction is not necessary here, see the first case in main

// variadic template, thus putting std::source_location at the end is not an option
template<std::size_t N, class... Args>
void log(StringWrapper<N> format_string, Args&&... args);

int main()
{
    log(StringWrapper{ "This {}, explicitly instantiating, but deducing the parameter" }, "works");
    log("This {}, could not match 'StringWrapper<N>' against 'const char *'", "fails");
}

What could be a possible workaround to this issue that would not require a change on the callsite?

One possibility is to use a std::string_view, however that would not give me access to N as a constant expression in the implementation of log and is thus not an option in my case.

Another possibility is to directly take a const char (&)[N], however then I am losing the std::source_location which is not an option either.

I understand why overload resolution would favor a function that takes a const char* over a function that takes a const char (&)[N], however I do not understand why 'StringWrapper' is not even attempted to match against 'const char (&)[N]' however.

CodePudding user response:

you can use class template argument deduction to do this (change log to object constructor instead of normal function)

template<std::size_t N, class... Args>
struct log{
    log(StringWrapper<N> src, Args&&... args){}
};

template<std::size_t N, class... Args>
log(const char(&)[N],Args&&...) -> log<N,Args...>;

template<std::size_t N, class... Args>
log(StringWrapper<N>,Args&&...) -> log<N,Args...>;

CodePudding user response:

Although you said you somehow need N inside log as constant expression, just in case someone reach here don't need it.

Change StringWrapper to non-template can also solve the problem.

struct StringWrapper {
    template<std::size_t N>
    consteval StringWrapper(const char(&format_string)[N], std::source_location source_location = std::source_location::current())
        : FormatString{format_string}, SourceInfo{ source_location }{}

    std::string_view FormatString;
    std::source_location SourceInfo{};
};

template<typename ... Args>
void log(StringWrapper src, Args&&...){
   auto N = src.FormatString.size(); // N, but not constant expression
}
  • Related