#include <type_traits>
template<bool Const>
struct view_tpl {
using value_type = std::conditional_t<Const, const int, int>;
value_type* ptr;
view_tpl() = default;
view_tpl(const view_tpl<false>& other) : ptr(other.ptr) { }
};
using view = view_tpl<false>;
using const_view = view_tpl<true>;
void read(const const_view& vw) { }
int main() {
view vw;
read(vw);
}
This code defines a const and a non-const view type, both as aliases to a view_tpl<Const>
template. It should be such that view
is implicitly convertible to const_view
, but not the other way around.
It Const
is true
, the defined copy-constructor enables this, and the compiler generates an additional default copy-constructor. If Const
is false
the defined copy-constructor replaces the default copy-constructor.
This implicit conversion should happen when f(vw)
is called.
It works correctly in the above code.
But if I add an argument to the templates (int N
), and turn f
and the two type aliasses into templates, it no longer works:
#include <type_traits>
template<int N, bool Const>
struct view_tpl {
using value_type = std::conditional_t<Const, const int, int>;
value_type* ptr;
view_tpl() = default;
view_tpl(const view_tpl<N, false>& other) : ptr(other.ptr) { }
};
template<int N> using view = view_tpl<N, false>;
template<int N> using const_view = view_tpl<N, true>;
template<int N>
void read(const const_view<N>& vw) { }
int main() {
view<0> vw;
read(vw);
}
Instead of doing the conversion of view_tpl<0, true>
to view_tpl<0, false>
, the compiler only tries a direct template substitution and fails:
main.cpp: In function 'int main()':
main.cpp:20:12: error: no matching function for call to 'read(view<0>&)'
20 | read(vw);
| ^
main.cpp:16:6: note: candidate: 'template<int N> void read(const_view<N>&)'
16 | void read(const const_view<N>& vw) { }
| ^~~~
main.cpp:16:6: note: template argument deduction/substitution failed:
main.cpp:20:12: note: template argument 'false' does not match 'true'
20 | read(vw);
| ^
Is there a way to make this work without changing too much of the code? (The real code is more complex than this example)
Unfortunately, implicit conversions won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can specify the template argument explicitly to by pass the deduction, then implicit conversion would work fine later. e.g.
view<0> vw;
read<0>(vw);
Or apply explicit conversion and wrap it into a helper.
template<int N>
void read(const view<N>& vw) { read(static_cast<const_view<N>>(vw)); }