How can I prevent implicit conversions in the function template?

How can I define a function template to prevent implicit conversions?

I seem to be able to prevent implicit conversions by using non-templated functions, but not using function templates.

Defining a forwarding reference function template as = delete

being too aggressive because it prevents invocation with non-const lvalue references.

Defining a function template with a const rvalue argument as =delete

[1] does not prevent implicit conversions.

Defining an rvalue overload for a specific type is how it =delete

works, but I would like to do it with templates.

Example of minimal code:

struct A {};

struct B {
  B() = default;

  B(const A&) {}
};

// Delete const rvalue reference.
template <class T>
void t_no_rvalue(const T&&) = delete; // 1

void t_no_rvalue(const B&) {}         // 2


// Delete forwarding reference.
template <class T>
void t_no_fwd_ref(T&&) = delete;     // 3

void t_no_fwd_ref(const B&) {}       // 4


// (non-template) Delete const rvalue reference.
void no_rvalue(const B&&) = delete;  // 5

void no_rvalue(const B&) {}          // 6


int main(int argc, char* argv[]) {
  A a;
  B b;

  // Undesired behaviour, implicit conversion allowed.
  t_no_rvalue(a);   // resolves to 2
  t_no_rvalue(b);   // resolves to 2

  // Undesired behaviour, invocation with non-const reference disallowed.
  t_no_fwd_ref(a);  // resolves to 3
  t_no_fwd_ref(b);  // resolves to 3

  // Desired behaviour.
  no_rvalue(a);     // resolves to 5
  no_rvalue(b);     // resolves to 6
}

      

My real use case is variant hashing, where implicitly converting a variant subtype back to a variant-like type will cause infinite recursion unless the hash function is specialized for all the constituents of the variant. However, the code example is clearer.

[1] Attempt at Why can I prevent implicit conversions for primitives but not user-defined types? but with broken code example.

+3


source to share


1 answer


The following overload will prevent implicit conversions:

template <class T>
void no_conversions(T) = delete; // 7

void no_conversions(const B&) {} // 8

      

and results in:

// Requested behaviour.
no_conversions(a); // resolves to 7
no_conversions(b); // resolves to 8

      



Value overloading poisons a set of implicit conversion overloads, since it will be an exact match.

Edit:

template <class T>
void no_conversions(const T&) = delete; // 9

void no_conversions(const B&) {}        // 10

      

works just as well.

+1


source







All Articles