3

C++ has this helpful feature that says that the template parameter is implicit in the code inside a template class A. However, for construction, this seems to clash with CTAD.

How can I make CTAD take precedence?

For example, here, there is an error in the f member because A is interpreted as A<T> where T is std::string, and not deduced from the parameter double.

#include<string>

template<class T>
struct A {
    A(T const& t) {}
    auto f() {return A{5.0};}  // error here triggered by line 11 `auto a2 = a1.f();`
};

int main() {
    A a1{std::string{"hello"}};
    auto a2 = a1.f();
}

https://godbolt.org/z/3nc7nev3o

alfC
  • 14,261
  • 4
  • 67
  • 118
  • 1
    Use template lambda inside `f`? Like [this](https://godbolt.org/z/Mrnd4oG15)? – Dmitry Jul 24 '23 at 08:48
  • @Dmitry, that is like having a `make_A` function, but thanks! – alfC Jul 24 '23 at 08:56
  • yeahhh, in fact I would not recommend to use it! User-defined deduction guides can easily [break](https://godbolt.org/z/Y666b1ded) the expected behaviour – Dmitry Jul 24 '23 at 09:20

2 Answers2

5

You need to use ::A to tell the compiler to use the name A from the global scope instead of using the name A in the class scope which is just shorthand for A<T>. That gives you:

#include<string>

template<class T>
struct A {
    A(T const& t) {}
    auto f() {return ::A{5.0};}  // uses CTAD to return A<double>
};

int main() {
    A a1{std::string{"hello"}};
    auto a2 = a1.f();
}

as seen in this live example.


Another option is to add a private alias to the class type and then use that alias in the function as needed. That gives you:

#include<string>

template<class T>
struct A {
private:
    template <typename U>
    using NewA = A<U>;
public:
    A(T const& t) {}
    auto f() {return NewA{5.0};}  // uses CTAD to return A<double>
};

int main() {
    A a1{std::string{"hello"}};
    auto a2 = a1.f();
}

and can bee seen working here

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Mmm, yes, that works. https://godbolt.org/z/hvG1r9EdT. However, now the class needs to know the namespace to which it belongs. Not ideal. – alfC Jul 23 '23 at 22:16
  • 1
    @alfC If you are defining the class, don't you know which namespace you are in? – NathanOliver Jul 23 '23 at 22:17
  • Yes, but if I later refactor the namespace, I have to change the internals. – alfC Jul 23 '23 at 22:18
  • 1
    @alfC I get that. I just added another option – NathanOliver Jul 23 '23 at 22:22
  • right, I discovered it at the same time. (version 2 in my answer). Cheers! – alfC Jul 23 '23 at 22:26
  • Unfortunately, the template typedef cannot be put inside the function, but nonetheless, this is the best option, IMO. – alfC Jul 23 '23 at 22:27
  • @alfC Yeah, it can't be at function scope but at least with it being a private alias no one else needs to know about it and you can use it in other functions in the same class if needed. – NathanOliver Jul 23 '23 at 22:30
  • In retrospect, our elders had `rebind` typedef for some classes (notably for allocators). This is just an evolution of it. This is a rebind upgraded to a template typedef and exploited by CTAD. https://godbolt.org/z/sPcMb3jWG . Perhaps `rebind_t` is a better name. Can be even be useful publicly. – alfC Jul 23 '23 at 22:33
1

It seems I can define a template typedef and hope the CTAD works through it.

#include<string>

template<class T>
struct A;

template<class T> using A_CTAD = A<T>;

template<class T>
struct A {
    A(T const& t) {}
    auto f() {return A_CTAD{5.0};}  // error here triggered by line 11 `auto a2 = a1.f();`
};

int main() {
    A a1{std::string{"hello"}};
    auto a2 = a1.f();
}

https://godbolt.org/z/hex79s91a

but it doesn't seem to be a big gain compared to using the good old make function. https://godbolt.org/z/j3PTW7MWY


Version 2, make an internal template typedef, not bad. No need for forward declarations.

template<class T>
struct A {
    A(T const& t) {}
private:
    template<class TT> using CTAD = A<TT>;
public:
    auto f() {return CTAD{5.0};}  // error here triggered by line 11 `auto a2 = a1.f();`
};
alfC
  • 14,261
  • 4
  • 67
  • 118