Search code examples
c++c++20string-viewstd-span

template argument deduce error for raw C string literal but works for std::string_view


I'm designing a PEG parser in C++, and the parser should support both std::string_view and std::span<Token> as the token stream input.

In the code, I see that one template class can only be instantiated by some code snippet like auto p2 = lit_(std::string_view("world"));, but not by the code auto p1 = lit_("world");. I mean the template deducing is not allowing the convertion from C string literal to std::string_view. Here is a simplified code to demonstrate the issue, it should be built by C++20.

#include <span>
#include <string_view>
#include <vector>

struct Token
{
};

template <typename T>
struct Viewer;

// explicit specialization for T = Token
template <>
struct Viewer<Token>
{
    using type = std::span<Token>; // std::span or derived class
};

// explicit specialization for T = char
template <>
struct Viewer<char>
{
    using type = std::string_view;
};

// alias template
template <typename T> using ViewerT = typename Viewer<T>::type;

template <typename Base, typename T>
struct parser_base {
    using v = ViewerT<T>;
    using charType = T;
};

// literal string match, for std::string_view, it could match a string
// for std::span<Token>, it will match a stream of Tokens defined by the span<Token>
template<typename V>
struct lit_ final : public parser_base<lit_<V>, typename V::value_type> {
    /// @brief Construct a lit_ parser.
    /// @param[in] str The string literal to parse.
    //template<typename V>
    constexpr lit_(V str) noexcept
        : str(str)
    {}

private:
    V str;
};


int main()
{

    //auto p1 = lit_("world");  // build error if uncommented
    auto p2 = lit_(std::string_view("world"));

    Token a;
    std::vector<Token> tokens;
    tokens.push_back(a);
    tokens.push_back(a);
    tokens.push_back(a);
    std::span<Token> match(tokens.begin(), tokens.size());
    auto p3 = lit_(match);  //
    return 0;
}

It demonstrates that both a stream of char(std::string_view) or the stream of Token(std::span<Token>) can be constructed as a lit_ (literal).

Any ideas on how to solve this issue?

Thanks.


Solution

  • You are correct that implicit conversions are not considered for template argument deduction.

    You may, however, use C++17 class template argument deduction.

    // (Place this after your class lit_ definition)
    
    // If constructed with a const char*, assume template is a std::string_view.
    lit_(const char*) -> lit_<std::string_view>;
    

    Now your commented line compiles.