A reference template parameter is deduced to be const or non-const depending on the argument. A reference template template parameter always ends up being non-const. Is there a way to fix it?
#include <concepts>
#include <print>
#include <type_traits>
#include <vector>
template<typename T, std::invocable<T> Visitor>
void ref_visit(T& t, const Visitor& visit) { visit(t); }
template<template<typename> typename C, typename T, std::invocable<T> Visitor>
void template_ref_visit(C<T>& c, Visitor visit) { visit(c[0]); }
struct Printer {
void operator()(int& i) const { std::println("non-const: {}", i); }
void operator()(const int& i) const { std::println("const: {}", i); }
} printer;
int main()
{
int i{1};
int& ri{i};
const int& cri{i};
ref_visit(ri, printer); // prints "non-const: 1"
ref_visit(cri, printer); // prints "const: 1"
std::vector<int> v{42};
std::vector<int>& rv{v};
const std::vector<int>& crv{v};
template_ref_visit(rv, printer); // prints "non-const: 42"
template_ref_visit(crv, printer); // FAILS TO COMPILE
}
Compiler output:
template_template_const.cpp: In function ‘int main()’:
template_template_const.cpp:29:24: error: binding reference of type ‘std::vector<int>&’ to ‘const std::vector<int>’ discards qualifiers
29 | template_ref_visit(crv, printer); // FAILS TO COMPILE
| ^~~
template_template_const.cpp:10:31: note: initializing argument 1 of ‘void template_ref_visit(C<T>&, Visitor) [with C = std::vector; T = int; Visitor = Printer]’
10 | void template_ref_visit(C<T>& c, Visitor visit) { visit(c[0]); }
|
Adding an overload
template<template<typename> typename C, typename T, std::invocable<T> Visitor>
void template_ref_visit(const C<T>& c, Visitor visit) { visit(c[0]); }
leads to code duplication. Can it be avoided?
T
in std::vector<T>
can be accessed through value_type
:
template<typename C, typename T = C::value_type, std::invocable<T> Visitor>
void template_ref_visit(C& c, Visitor& visit) { visit(c[0]); }