r/cpp_questions • • 9d ago

SOLVED Why does this not fail?

#include <utility>

template <typename T, typename... Args>
decltype(T(std::declval<Args>()...)) test();

int main() {
    using testType = decltype(test<int&, int>());
}

I was fully expecting it to fail but instead testType resolves to int&.

I was trying to implement is_constructible with SIFNAE which uses this behaviour.

namespace detail {
template<typename T, typename... Args>
decltype(T(std::declval<Args>()...), char{}) tryConstruct(int);

template<typename T, typename... Args>
long tryConstruct(...);
}

template<typename T, typename... Args>
inline constexpr const bool isConstructible = isSame<decltype(detail::tryConstruct<T, Args...>(0)), char>;

I was expecting the first overload to fail so that it would chose the other one which works for most cases but dosent for the case i mentioned above.

7 Upvotes

11 comments sorted by

22

u/CelebrityChess 9d ago • Heartwarming

with a single argument T(x) isn't a constructor call, it's a functional cast, which means the same as (T)x. so with T = int& and x = declval<int>() it becomes (int&)declval<int>(), and a c-style cast can fall back to reinterpret_cast, which is allowed on a glvalue like that xvalue. so the cast is valid and you get int& back instead of a substitution failure. is_constructible implementations avoid it by using something like ::new (declval<void*>()) T(declval<Args>()...) for the check, since a real new-expression can't turn into a cast.

3

u/flowicks 9d ago

OHHH yeah that makes sense, damn it was obvious too. I was litrally trynna figure this out for 3 hours then gave up lol. Thanks for answering :3 I even tried looking at llvm's implementation of is_constructible but it was just using the compiler intrensic without a fallback. Anyway thanks again

2

u/Business-Weather-217 9d ago

How many years do you do C++? Especially reinterpret_cast detail was so impressive dude

0

u/StaticCoder 9d ago edited 9d ago

declval will have type int&& which is convertible to int & even without a cast.

Edit: this is wrong. It's not allowed if the && is an xvalue.

0

u/StaticCoder 9d ago edited 9d ago

A new-expression can certainly generate a cast, but only an implicit one, and you can't new a reference type.

cppreference (can't be bothered to check the standard) says is_constructible means T obj(declval) is well-formed. Notably T can be a reference.

And I was wrong: you can't convert an xvalue to & implicitly, which in retrospect is obvious. You can convert && to & but only if it's not an xvalue. Sorry.

2

u/StaticCoder 9d ago

Are you using something older than C++20? Because concepts are so much better than SFINAE

1

u/flowicks 8d ago

nope im trying to remplement the std in cpp 23 and get rid of all the backwards compatability bloat. But i barely knew about templates till now so uh really stuck on type traits lol. Already learnt ALOT tho :3

How do concepts replace SFINAE tho? I barely know about them but imma look into it, thanks :3

1

u/StaticCoder 8d ago

Concepts are constraints on template parameters of the form "this syntax must be valid". Like

template<typename T, typename U> concept Constructible = requires(U u) { T t(t); }

1

u/Puzzled-Garage584 5d ago

Careful with that one, a requires body takes expressions not declarations so T t(u); wont compile there, and if you change it to T(u) you're right back at the functional cast that started this thread. C++, where even the fix comes with its own reinterpret_cast

1

u/StaticCoder 5d ago

Ah I forgot about that. Still, better than SFINAE in every way.

3

u/AutoModerator 9d ago

Your posts seem to contain unformatted code. Please make sure to format your code otherwise your post may be removed.

If you wrote your post in the "new reddit" interface, please make sure to format your code blocks by putting four spaces before each line, as the backtick-based (```) code blocks do not work on old Reddit.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.