Search code examples
c++templatestemplate-argument-deduction

Template deduction guide doesn't work well with aggregate initialization


I am studying how to use deduction guides. I composed this program.

template<typename T>
struct Test {
    void f() {
        cout << std::is_same_v<T, std::size_t> << endl;
    }
    T t;
};

Test(int) -> Test<std::size_t>;

int main() {
    Test t1(1.2);
    t1.f();
}

I expected the deduction guide will only kick in when if I pass an integer to the aggregate initialization. Since I pass 1.2, I expected T will be deducted to double which won't triggered the deduction guide. However the output is 1, i.e. T -> int -> size_t.

I tried to add a constructor to the struct.

template<typename T>
struct Test {
    void f() {
        cout << std::is_same_v<T, std::size_t> << endl;
    }
    Test(T v): t(v) {}
    T t;
};

Test(int) -> Test<std::size_t>;

int main() {
    Test t1(1.2);
    t1.f();
}

The output was 0 which matched my previous expectation. Comparing these two programs, the only difference here is how the struct got initialized. What does the deduction guide not work well with the aggregate initialization? Thanks.


Solution

  • Deduction guide works like a overload resolution for a function.

    So when you have written deduction guide like this:

    Test(int) -> Test<std::size_t>;
    

    When constructing Test wiht deduction type, compiler will do:

    • find all list of deduction guides (you have one)
    • check if exact match of arguments types is possible (the is none)
    • check deduction guide can be used by promoting integer or floating point types (your case none) types of arguments.
    • check if deduction guide can be called applying some implicit conversion (here double -> int kick in).
    • if nothing worked try use deduce T by applying aggregate intilization.

    So you have to create deduction overload which will kick in before conversion or promotion is applied. You need exact match. So solution is simple add template deduction gude which will catch everything else:

    
    template<typename T>
    Test(T) -> Test<T>;
    Test(int) -> Test<std::size_t>;
    

    https://godbolt.org/z/rhqP146jn

    Now compiler when he can't find exact match without template, but can use tempted deduction guide which lead to perfect argument type match.