deduction guides and injected class names
Asked Answered
A

2

9
template <typename T>
struct X
{
    template <typename Iter>
    X(Iter a, Iter b) {}

    template <typename Iter>
    auto f(Iter a, Iter b)
    {
        return X(a, b);
    }
};

In the "C++ Templates, The Complete Guide" 2nd edition, there was the previous example about the subtitles of implicit deduction guides with injected class names. The author mentioned that class argument deduction is disabled for injected class names because the type of the return of f would be X<Iter> due to the implicit deduction guide. But I believe the implicit deduction guide for a template constructor would rather look like the one below.

  template <typename T, typename Iter>
  X(Iter a, Iter b) -> X<T>;

My question is how would the class template argument types even be deduced in that case T and Iter are two distinct types and the parameters types only rely on Iter. Also even if T could be deduced somehow, T and Iter are independent so deducing Iter from the arguments should not imply that X has type X<Iter> right? Is this some error with the text in the book or should the deduction guide look different from what I thought?

Alfons answered 3/7, 2018 at 7:33 Comment(0)
K
6

You are correct. The implicitly generated deduction guide would indeed look like the one you wrote. And template argument deduction could never deduce T from it. That indeed won't cause a problem. The problem is with user supplied deduction guides. Like this:

template <typename Iter>
X(Iter a, Iter b) -> X<typename Iter::value_type>;

Which are often added to allow class template argument deduction from iterators. That one could wreak havoc if the injected class name did not suppress argument deduction. The authors may have overlooked the need to add that deduction guide to demonstrate the problem.

Here's an illustration of the problem:

auto v = std::vector<int>{1, 2};
auto x1 = X<float>(begin(v), end(v));
auto x2 = x1.f(begin(v), begin(v));

What's the type of x2? If we read the class template definition, we expect it to be X<float> as it would be in C++14, but if class template argument deduction isn't turned off and we add our deduction guide, we'll get X<int>!

Imagine existing code bases where types suddenly shifted after moving to C++17. That would be very bad.

Krever answered 3/7, 2018 at 7:46 Comment(0)
F
0

I do NOT think the authors have to add any deduction guides to demonstrate the problem here. In my opinion, we can rewrite the definition of X<T>::f as follows:

template <typename Iter>
auto f(Iter b, Iter e){
    X ret(b,e);
    return ret;
}

If C++ don’t disable class template argument deduction (CTAD) here, this initialization of ret is where CTAD will kick in and gets the type X<Iter>. In Unslander Monica’s example above, x2 will have a more complicated type X<typename std::vector<int>::iterator>. This is why the authors don’t elaborate on it with deduction guides.

Faulk answered 5/2, 2023 at 1:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.