-
Notifications
You must be signed in to change notification settings - Fork 16.4k
Description
The compiler behaves differently depending on whether the same requires clause for a function template is written between the template parameters and the function signature or after the function signature. In the former case it performs short-circuit evaluation (and agrees with gcc) whereas in the latter case it doesn't, leading to infinite recursion in my case.
However, short-circuit doesn't misbehave in all cases, since I didn't manage to hit the issue in a simpler case I tested (see below my case), so it doesn't seem to be related only to short-circuit evaluation, but concept evaluation as well.
The issue involves a struct Thing with a constructor template with a single argument, constrained by std::copy_constructible<T>, that could possibly be a better match than the copy constructor (when T = Thing). I expected that changing the contraint to !std::is_same_v<T, Thing> && std::copy_constructible<T> would solve the issue, but that worked or not depending on the location in which I wrote the requires clause.
Possibly related to #44304
My case (reproduction): Compiler Explorer
#include <concepts>
#include <type_traits>
struct Thing
{
Thing() = default;
Thing(const Thing&) = default;
#ifdef REQUIRES_AFTER_TEMPLATE
// Works (expected)
template <typename T>
requires (!std::is_same_v<T, Thing> && std::copy_constructible<T>)
Thing(const T& /*t*/)
#else
// Does NOT work (UNEXPECTED)
// Conjunction short-circuit does not
// seem to happen correctly:
// https://en.cppreference.com/w/cpp/language/constraints#Conjunctions
template <typename T>
Thing(const T& /*t*/)
requires (!std::is_same_v<T, Thing> && std::copy_constructible<T>)
#endif
{}
};
void test(int a)
{
auto thing = Thing(a);
// This one breaks, when checking if
// `Thing` models `std::copy_constructible`, because
// the compiler tries to check
// if the possibly-copy-constructor is a valid
// copy constructor, which leads to checking
// its constraints, therefore checking `std::copy_constructible` again,
// which leads to infinite recursion
[[maybe_unused]] auto thing_of_thing = Thing(thing);
}Simple case, no issues: Compiler Explorer
#include <concepts>
#include <type_traits>
template <typename T>
struct Empty
{};
template <typename T>
constexpr bool breaks_if_instantiated = Empty<T>::value;
struct Thing
{
Thing() = default;
#ifdef REQUIRES_AFTER_TEMPLATE
// Works (expected)
template <typename T>
requires (!std::is_same_v<T, Thing> && breaks_if_instantiated<T>)
Thing(const T& /*t*/)
#else
// Works (expected)
template <typename T>
Thing(const T& /*t*/)
requires (!std::is_same_v<T, Thing> && breaks_if_instantiated<T>)
#endif
{}
};
void test(int a)
{
// Breaks (expected)
//auto thing_with_int = Thing(a);
Thing thing{};
[[maybe_unused]] auto thing_with_thing = Thing(thing);
}Metadata
Metadata
Assignees
Labels
Type
Projects
Status