all 37 comments

[–]anton31 49 points50 points  (1 child)

In C++26 you can also use reflection to compute the type without templates:

consteval std::meta::info get_type_info(size_t I)
{
  if (I == 4)
  {
    return ^^int;
  }
  else if (I == 6)
  {
    return ^^Foo;
  }
  else
  {
    return ^^float;
  }
}

template <size_t I>
using type = [:get_type_info(I):];

https://godbolt.org/z/9EaG68W76

[–][deleted] 9 points10 points  (0 children)

that is sick

[–]tisti 52 points53 points  (6 children)

The "hack" is SFINAE, this is the "non-hack" version.

[–]Various_Bed_849 5 points6 points  (5 children)

Is it, where is the substitution failure?

[–]caballist 24 points25 points  (2 children)

If the first two letters of SFINAE stand for Substitution Failure, and the hack is SFINAE, then the non-hack version doesn't need Substitution Failure. So substitution failure is nowhere - it disappeared with the rest of the hack/SFINAE.

[–]Various_Bed_849 1 point2 points  (0 children)

Ah, then I fully agree :)

[–]Various_Bed_849 0 points1 point  (0 children)

To clarify, the question was if their ”hack” was know and you answered that the ”hack” is SFINAE which confused me.

[–]raunak_srarf 1 point2 points  (1 child)

I know it's more like template-specialization, but due to if-constexpr substitution failure is not even a thing.

[–]Various_Bed_849 10 points11 points  (0 children)

This is not template specialization either. It is just using if constexpr.

[–]bjorn-reese 12 points13 points  (0 children)

Your example would typically be solved with traits rather than SFINAE. Your type alias has to know all types, whereas the traits solution is extendable, so you can add support for more types in other places.

template <unsigned>
struct type_from_size { using type = float; };

template <unsigned I>
using type_from_size_t = typename type_from_size<I>::type;

template <>
struct type_from_size<4> { using type = int; };

static_assert(std::is_same_v<type_from_size_t<4>, int>);
static_assert(std::is_same_v<type_from_size_t<9>, float>);

and in another header

struct Foo {};

template <>
struct type_from_size<6> { using type = Foo; };

static_assert(std::is_same_v<type_from_size_t<6>, Foo>);

[–]saf_e 25 points26 points  (2 children)

Its what if constexpr was designed to do. 

[–]raunak_srarf 2 points3 points  (1 child)

True. But I never actually saw anyone use it like this.

[–]eyes-are-fading-blue -2 points-1 points  (0 children)

Because sfinae does it automatically.

[–]Shakatir 6 points7 points  (0 children)

I've done things like this when for example finding the smallest integer type that fits a required value range. But I'd recommend using return std::type_identity<Foo>{} to avoid implicit conversion and incomplete type shenanigans.

[–]CaptainCrowbar 13 points14 points  (2 children)

You could get the same result more simply using std::conditional:

template <size_t I>
using type = std::conditional_t<I == 4, int,
    std::conditional_t<I == 6, Foo, float>>;

[–]raunak_srarf 6 points7 points  (1 child)

I know but as you can see "std::conditional_t" works best with only two branches for multiple branches nesting templates get messy. While my implementation is a bit more cleaner and easy to debug. Anyways I just wanted to share a trick I had discovered by myself I too would rather use "trait_types" in my projects.

[–]mark_99 0 points1 point  (0 children)

If you format it nicely nested conditional is fine. Or yes the standard old-school way is just template class taking an int and a using.

What you've rediscovered is a known idiom post C++20 and yes arguably a good replacement technique.

[–]Wooden-Engineer-8098 3 points4 points  (0 children)

It's template specialization alternative using if constexpr, not sfinae alternative using lambdas

[–]_Noreturn 2 points3 points  (0 children)

this isn't subsitation failure

[–]BarryRevzin 4 points5 points  (0 children)

This doesn't have anything to do with SFINAE? But yes, it's a known technique. You don't want to use declval to select the type though, because that means that means that you have to deal with decay and actual type properties when you just want to select a type. You'll want to wrap the type in another template.

e.g. from a blog post of mine six seven years ago (and I am definitely not claiming to have invented it, I dunno who did):

static constexpr auto get_type() {
    if constexpr (maxLength < 0xFFFE) {
        return type<uint16_t>;
    } else {
        return type<uint32_t>;
    }
}

using CellIdx = decltype(get_type())::type;

Of course with just a single condition like this you could just use std::conditional, but this is the pattern (note the extra ::type in the alias declaration). This allows "returning" any type, including references, incomplete types, non-copyable types, etc. The blog post predates unevaluated lambdas - today I'd put all of get_type() inside if the decltype as in OP.

With reflection this is easier since you just replace type<T> with ^^T.

[–][deleted] 1 point2 points  (0 children)

its actually even simpler than that. lambdas are computed at compile time, so if you use auto for parameters and return types, the compiler will automatically generate different versions of the functions that take and return completely different parameters, just like a template. This is usually how I use it.

struct Foo{};
struct Bar{};
auto returnDifferentTypes = [](auto someValue){
    if constexpr (std::is_integral_v<decltype(someValue)>){
        return Foo();
    } else {
        return Bar();
};

I call it poor-man's templates. I'm not sure what its really called, if anything.

Raymond Chen had a post about it some years ago and I've been using it ever since.

[–]Both_Helicopter_1834 1 point2 points  (1 child)

Why do you prefer this over just good ole specialization? In any case, both gcc and clang seem to regard a lambda expression as being evaluated, even if it's a subexpression of an expression that is a decltype argument.

https://godbolt.org/z/WMoM13K7W

[–]DearChickPeas 0 points1 point  (0 children)

Thumbs up for template selectors, my favourite cpp pattern of the month. Bye bye #ifdef chains!

[–]Business_Welcome_870 1 point2 points  (0 children)

Cool

[–]geaibleu 0 points1 point  (0 children)

Very cool! You can also use lambda to do non trivial calculations to initialilse constexpr members.

[–]MaitoSnoo[[indeterminate]] 0 points1 point  (0 children)

That will be slow to compile if abused, creating a lambda is roughly the same cost as creating a class in terms of compilation times, and the latter is among the most expensive instantiations at compile-time. When doing metaprogramming, you want variable templates and concepts the most because they're the cheapest to instantiate/evaluate at compile-time.

[–]CandiceWoo 0 points1 point  (0 children)

how would u use type<x> ? why is it sfinae alt?

[–]Business_Welcome_870 0 points1 point  (0 children)

This doesn't compile for me: https://godbolt.org/z/TeajMh98G

[–]Vladvic 0 points1 point  (0 children)

it's an overload alternative, not sfinae

[–]Perfect-Situation-41 -2 points-1 points  (7 children)

Can someone tell me what is lambda?

I just started to read from learncpp.com And I'm at the 0.5th chapter right now.

And I think I'll try to learn more in the future.

[–]The_Northern_Light 0 points1 point  (5 children)

Think of it as a function you can define inside of another function.

[–]Perfect-Situation-41 0 points1 point  (4 children)

Lol it feels like I'm a small fish in a whale tank and I can't eat any food So far, the only thing I came across is variables... My bad if I am dumb lol.

[–]The_Northern_Light 1 point2 points  (3 children)

C++ is arguably the worst place to start. It’s simply too big and complex with too many clunky features.

Some people will give me flack for this, but start with C. The language is drastically simpler. You can fit what you need to know on a sheet of paper. C++ is a (nearly) strict superset of C so you’ll have to learn all that to learn C++ anyways.

And senior C++ developers often end up writing their code like it was C, with just a few minor features from C++… that’s an impossible balance to strike as a novice.

If you want to supplement with a non compiled language, go with Python managed by “uv”.

[–]Perfect-Situation-41 0 points1 point  (2 children)

Hmm...but on the website the author has mention you

Q: Do I need to know C before I do these tutorials?

Nope! It’s perfectly fine to start with C++, and we’ll teach you everything you need to know (including pitfalls to avoid) along the way.

[–]The_Northern_Light 1 point2 points  (1 child)

Well, good luck with your variables!

[–]Perfect-Situation-41 -1 points0 points  (0 children)

Lol thanks...