1

I have the following piece of code:

template<class A>
struct X {
        template<class T>
        X(T t) {}
};

template<class A>
void f(X<A> x, A a) {}

int main() {
        f(0, 0);
}

which fails to compile (gcc 14.1.1) with the following error:

test.cc:20:10: error: no matching function for call to ‘f(int, int)’
   20 |         f(0, 0);
      |         ~^~~~~~
test.cc:17:6: note: candidate: ‘template<class A> void f(X<A>, A)’
   17 | void f(X<A> x, A a) {}
      |      ^
test.cc:17:6: note:   template argument deduction/substitution failed:
test.cc:20:10: note:   mismatched types ‘X<A>’ and ‘int’
   20 |         f(0, 0);
      |         ~^~~~~~

If I understand correctly, any value should be convertible to X<A> because of the templated constructor X(T) What's interesting though is that the static_assert doesn't cause a compile-time error:

static_assert(std::is_convertible_v<int, X<int>>); // no assertion failure

How can I change the program to keep both templates and allow conversions to X?

3
  • 2
    You can't deduce A in X<A> from 0. What you can do it to disable deduction for X<A> using a standard trick: void foo(std::type_identity_t<X<A>> x, A a). Commented Jul 6, 2024 at 17:44
  • 1
    Questions are for questions, not solutions. Solutions fall under the "answer" category. You can answer your own question. Commented Jul 6, 2024 at 19:11
  • Implicit conversions are not considered during template argument deduction. Commented Jul 7, 2024 at 6:04

1 Answer 1

0

This is template type deduction, and it will not try to deduce a type which your input is convertible to.

A simpler example shows the problem, and also shows why it is a problem:

#include <type_traits>

struct Foo {};

template<typename T>
struct Bar {
    Bar(Foo);
};

template<class A>
void f(Bar<A>) {}

int main() {
    static_assert(std::is_convertible_v<Foo, Bar<int>>);
    static_assert(std::is_convertible_v<Foo, Bar<double>>);
    f(Foo{});
}

How can the compiler deduce A? It could be int, double, or anything else, as Bar<whatever> is convertible to Foo anyway.


Back to your case, you say that

    static_assert(std::is_convertible_v<int, X<int>>); // no assertion failure

Yes, but this passes too:

    static_assert(std::is_convertible_v<int, X<double>>); // no assertion failure

so why should the compiler deduce int instead of double?

You might think that the compiler can be 100% sure of A being int by doing argument deduction on the second argument, and so be happy with X<int> for the first parameter type. That's not possible because the deduction happens independently for every arg-param pair.

Sign up to request clarification or add additional context in comments.

3 Comments

If a compiler can't deduce A from X<A>, why doesn't it simply ignore this failure ("deduction failure is not an error") and not deduce A to int from the second argument?
I'm think of specializations of X. That would create a problem, I think. I'll give a look at a book.
@Evg, and I understand correctly, each arg-param pair is analysed in isolation.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.