I just watched Stephan T. Lavavej talk at CppCon 2018
on "Class Template Argument Deduction", where at some point he incidentally says:
In C++ type information almost never flows backwards ... I had to say "almost" because there's one or two cases, possibly more but very few.
Despite trying to figure out which cases he might be referring to, I couldn't come up with anything. Hence the question:
In which cases the C++17 standard mandates that type information propagate backwards?
Here is at least one case:
struct foo { template<class T> operator T() const { std::cout << sizeof(T) << "\n"; return {}; } };
if you do foo f; int x = f; double y = f;
, type information will flow "backwards" to figure out what T
is in operator T
.
You can use this in a more advanced way:
template<class T> struct tag_t {using type=T;}; template<class F> struct deduce_return_t { F f; template<class T> operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); } }; template<class F> deduce_return_t(F&&)->deduce_return_t<F>; template<class...Args> auto construct_from( Args&&... args ) { return deduce_return_t{ [&](auto ret){ using R=typename decltype(ret)::type; return R{ std::forward<Args>(args)... }; }}; }
so now I can do
std::vector<int> v = construct_from( 1, 2, 3 );
and it works.
Of course, why not just do {1,2,3}
? Well, {1,2,3}
isn't an expression.
std::vector<std::vector<int>> v; v.emplace_back( construct_from(1,2,3) );
which, admittedly, require a bit more wizardry: Live example. (I have to make the deduce return do a SFINAE check of F, then make the F be SFINAE friendly, and I have to block std::initializer_list in deduce_return_t operator T.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With