My attempt at an immutable container view in C++ I have this very simple template:

#include &lt;algorithm&gt;
#include &lt;initializer_list&gt;
#include &lt;iostream&gt;
#include &lt;iterator&gt;
#include &lt;list&gt;
#include &lt;set&gt;
#include &lt;vector&gt;

template&lt;class Container&gt;
class ImmutableContainerView {
private:
    Container&amp; my_container;
public:
    ImmutableContainerView(Container&amp; container) :
    my_container {container} {}

    typename Container::const_iterator cbegin() const {
        return my_container.cbegin();
    }

    typename Container::const_iterator cend() const {
        return my_container.cend();
    }

    typename Container::const_reverse_iterator crbegin() const {
        return my_container.crbegin();
    }

    typename Container::const_reverse_iterator crend() const {
        return my_container.crend();
    }

    typename Container::size_type size() const {
        return my_container.size();
    }

    bool empty() const {
        return my_container.empty();
    }
};

int main(int argc, const char * argv[]) {
    std::initializer_list&lt;int&gt; il = { 1, 2, 4, 5, 6 };

    std::list&lt;int&gt;   my_list   = il;
    std::set&lt;int&gt;    my_set    = il;
    std::vector&lt;int&gt; my_vector = il;

    ImmutableContainerView&lt;std::list&lt;int&gt;&gt;   my_immutable_list(my_list);
    ImmutableContainerView&lt;std::set&lt;int&gt;&gt;    my_immutable_set(my_set);
    ImmutableContainerView&lt;std::vector&lt;int&gt;&gt; my_immutable_vector(my_vector);

    // Iterators:
    std::ostream_iterator&lt;int&gt; out_it (std::cout, ", ");
    std::copy(my_immutable_list.cbegin(), my_immutable_list.cend(), out_it);
    std::cout &lt;&lt; "\n";

    std::copy(my_immutable_set.cbegin(), my_immutable_set.cend(), out_it);
    std::cout &lt;&lt; "\n";

    std::copy(my_immutable_vector.cbegin(), my_immutable_vector.cend(), out_it);
    std::cout &lt;&lt; "\n";

    // Reversed iterators:
    std::copy(my_immutable_list.crbegin(), my_immutable_list.crend(), out_it);
    std::cout &lt;&lt; "\n";

    std::copy(my_immutable_set.crbegin(), my_immutable_set.crend(), out_it);
    std::cout &lt;&lt; "\n";

    std::copy(my_immutable_vector.crbegin(), my_immutable_vector.crend(), out_it);
    std::cout &lt;&lt; "\n";

    // Size:
    std::cout &lt;&lt; my_immutable_list.size() &lt;&lt; "\n";
    std::cout &lt;&lt; my_immutable_set.size() &lt;&lt; "\n";
    std::cout &lt;&lt; my_immutable_vector.size() &lt;&lt; "\n";

    // Empty:
    std::cout &lt;&lt; std::boolalpha;
    std::cout &lt;&lt; my_immutable_list.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; my_immutable_set.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; my_immutable_vector.empty() &lt;&lt; "\n";

    return 0;
}


I would like to hear comments on how to improve it. In particular, is it possible to do some metaprogramming trickery such that it can adapt to different containers; for example, it could provide operator[size_t] for std::vector, but find for std::set?
Your view isn't immutable, though immutability might be a property of the underlying object. It's a constant view, meaning the underlying object might change, but not from that view. The canonical way to get a constant view is by the way to use a constant reference, unless the view should erase some of the differences between underlying containers, like std::string_view does.

You are missing .begin(), .end(), .rbegin() and .rend().
Your view is constant, fine, but forcing everyone to explicitly say so for each function-call breaks interface conventions.
The missing functions are the common interface, the ones you provided just optional convenience-functions for calling manually which might not be provided even if possible, and certainly never used in generic code. Take a look at &lt;iterator&gt; and the free functions for accessing any generic range there.