I have written a class called small_vector<typename T, size_t N>
where T is the value_type and N is the bucket size. I had a view
class defined in it:
template <typename T, size_t N>
class small_vector {
....
class view {
...
};
};
Not I want to customize a formatter for it, I wrote like this:
template<typename T, size_t N>
using small_vector_view = typename small_vector<T, N>::view;
template<typename T, size_t N> struct std::formatter<typename small_vector<T, N>::view, char> {
enum class Style { Default, Compact, Pretty };
Style style = Style::Default;
constexpr auto parse(format_parse_context &ctx)
{
auto it = ctx.begin();
if (it == ctx.end() || *it == '}')
return it;
switch (*it) {
case 'c':
style = Style::Compact;
break;
case 'p':
style = Style::Pretty;
break;
default:
throw format_error("invalid format specifier for small_vector");
}
return ++it;
}
auto format(const typename small_vector<T, N>::view &v, format_context &ctx) const
{
if (v.empty()) {
if (style == Style::Pretty) {
return format_to(ctx.out(), "[\n]");
}
return format_to(ctx.out(), "[]");
}
std::string result;
if (style == Style::Pretty) {
result = "[\n ";
}
else {
result = "[";
}
for (size_t i = 0; i < v.size(); ++i) {
if (i > 0) {
if (style == Style::Pretty) {
result += ",\n ";
}
else if (style == Style::Compact) {
result += ",";
}
else {
result += ", ";
}
}
if constexpr (std::is_same_v<T, std::string>) {
result += std::format("\"{}\"", v[i]);
}
else {
result += std::format("{}", v[i]);
}
}
if (style == Style::Pretty) {
result += "\n]";
}
else {
result += "]";
}
return format_to(ctx.out(), "{}", result);
}
};
But it won't compile, and the compiler complains:
D:\repos\ustring\small_vector.h(602): error C2764: 'T': template parameter not used or deducible in partial specialization 'std::formatter<small_vector<T,InlineCapacity>::view,char>'
D:\repos\ustring\small_vector.h(602): error C2764: 'N': template parameter not used or deducible in partial specialization
'std::formatter<small_vector<T,InlineCapacity>::view,char>'
D:\repos\ustring\small_vector_tests.cpp(557): error C7595: 'std::basic_format_string<char,small_vector<int,16>::view &>::basic_format_string': call to immediate function is not a constant expression
I'm using the newest MSVC on x64 Windows, I wonder why it cannot deduce T and N, how can I make this piece of code compile?
(full code: https://github.com/nekomiya-kasane/temp-utils/blob/main/small_vector.h, last 2 functions)
T
and N
are used in an non-deduced context, and can therefore not be deduced.
See Non-deduced contexts (first bullet point):
If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
- The nested-name-specifier (everything to the left of the scope resolution operator
::
) of a type that was specified using a qualified-id
in your case:
// /--------- qualified-id --------\
// | |
struct std::formatter<typename small_vector<T, N>::view, char>
// | |
// \ nested-name-spec./
// (non-deduced)
See Partial specialization of a class for a nested class of a template class for a potential workaround.
In your case you could work around this by adding an alias to your small_vector::view
that resolves to the small_vector
that contains it, e.g.:
template<class T, std::size_t N>
class small_vector {
// ...
public:
class view {
// ...
public:
using small_vector_type = small_vector;
};
};
... then you can write a concept that checks if a given type is a small_vector::view by checking the small_vector_type alias:
template<class T>
constexpr inline bool is_small_vector_v = false;
template<class T, std::size_t N>
constexpr inline bool is_small_vector_v<small_vector<T, N>> = true;
template<class T>
concept small_vector_view_type = requires() {
// has a small_vector_type alias
typename T::small_vector_type;
// small_vector_type is a small_vector
requires is_small_vector_v<typename T::small_vector_type>;
// small_vector_type::view is equivalent to T
requires std::same_as<typename T::small_vector_type::view, T>;
};
...and then this concept can be used to write a partial specialization for small_vector::view:
template<small_vector_view_type T>
struct std::formatter<T, char> {
template<class ParseContext>
constexpr ParseContext::iterator parse(ParseContext& ctx) { /* ... */ }
template<class FmtContext>
FmtContext::iterator format(T const& view, FmtContext& ctx) const { /* ... */ }
};