Jason R Jason R - 1 month ago 5x
C++ Question

Is there a canonical way to allow implicit conversion of a non-const template argument type to a const one?

I have a function that takes arguments of a specific template type; a simplified version might look like:

#include <type_traits>

template <typename T>
struct foo
// default constructor
foo() { }

// simple copy constructor that can construct a foo<T> from a foo<T>
// or foo<const T>
foo(const foo<typename std::remove_const<T>::type> &) { }

behaves similar to a
, with some other addon functionality that isn't relevant to this question. The semantics of the function dictate that it prefers to take in a
foo<const T>
foo<const T>
is implicitly constructible from
, so I would like to be able to do something like the following:

template <typename T>
void bar(foo<const T> f) { }

int main()
bar(foo<const int>()); // fine
bar(foo<int>()); // compile error

This fails because there are no matching overloads for
that take a
(even though a
foo<const int>
could be implicitly constructed from a
, the overload resolution in concert with template instantiation seems to be stricter than that.

Is there a canonical way to accomplish this? I know that I could introduce a second overload for
that takes a
and dispatches manually to
bar(foo<const T>)
, but I'd like to avoid the duplication if possible.


The reason you code doesn't work is because implicit conversion is apply after template argument deduction. So in that case, foo<int> indeed won't match foo<const T>, and the compiler won't able to deduce what the T is. You can try by yourself to check specify the type directly:

int main()
    bar(foo<const int>()); // fine
    bar<int>(foo<int>());  // also fine

What you can do is to let the compiler take any type:

template <typename T> // T might be foo<int>
void bar(T f) { }

Or if you want, you can let the compiler deduce the inner T without the const:

template <typename T> // can deduce T as const int
void bar(foo<T> f) { }

If you really want to enforce constness (even in generic code), you might want to add a utility function to your class, something like this:

foo<const T> as_const() const { return *this; }

So when you use a generic function, you can send is a const version of your class: