c++templates

C++: preserve const-ness of a template template parameter


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?


Solution

  • 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]); }