-
Notifications
You must be signed in to change notification settings - Fork 439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[concept] Swappable bug & concepts::valid_expr #29
Comments
Yes the struct Swappable
{
template<typename T>
auto requires_(T && t) -> decltype(
concepts::valid_expr(
(swap((T&&)t, (T&&)t), 42)
));
template<typename T, typename U>
auto requires_(T && t, U && u) -> decltype(
concepts::valid_expr(
(swap((T&&)t, (U&&)u), 42),
(swap((U&&)u, (T&&)t), 42)
));
}; This will prevent the BTW, because its easy to slip and forget to add the magic struct void_ {};
template<class T> T&& operator,(T&& x, void_);
template<typename T>
int valid_expr(T &&);
#define TICK_VALID(...) decltype(tick::detail::valid_expr((__VA_ARGS__, tick::detail::void_()))) I know its an extra macro, which people seem to not like, but I just found it too easy to forget the |
@pfultz2 , I don't see how |
It doesn't apply in this context, but it is needed when using other constructs such as However, after playing around with this more, I realized that the |
Decltype on which valid expression? I use valid_expr so that each valid expression can be a separate expression as a parameter to the function, without weird interference between expressions. |
And yes, Swappable is buggy. I'll fix it. Thanks for the report. |
Sorry, my bad, actually
I think we mean that the concept, for example, struct EqualityComparable
{
template<typename T>
auto requires_(T && t) -> decltype(
concepts::convertible_to<bool>(t == t),
concepts::convertible_to<bool>(t != t)
);
};
Isn't the problem with functions returning Furthermore, I have yet to run into an issue with commas(perhaps, it would be really difficult to figure out if I did), but I have run into problems quite a lot with using |
Yes, but to write a strictly correct concept, you'll need to guard against strange comma operators, even though they're vanishingly rare. And in guarding against it, you'll make your code verbose. The use of |
Do you have an example where this would fail? I am having trouble coming up with a test case where this would fail. |
Nm, I found a test case. If the return type of the function is noncopyable, and the comma operator was overloaded by value, this would incorrectly cause a substitution failure. |
Ok well as a partial solution you could start with a special object with the comma overloaded to prevent other comma overloads, something like this: struct Swappable
{
template<typename T>
auto requires_(T && t) -> decltype(
comma_guard(),
swap((T&&)t, (T&&)t)
);
}; So the struct comma_guard
{
template<class T>
const comma_guard& operator,(T&&) const;
}; So this works on gcc, but doesn't on clang. Its an ambiguous overloads on clang, which shouldn't be since the member function should be preferred. Unless I am missing something. |
Ok sorry to spam the list, but I solved it now. If the struct comma_guard
{
struct any
{
template<class T>
any(T&&);
};
const comma_guard& operator,(any) const;
}; It will work, because clang doesn't consider it a class member when its templated. |
|
Thanks, you're right. I seemed so close to a solution. So I guess there are two ways to implement concept predicates. The first way requires using struct Swappable
{
template<typename T>
auto requires_(T && t) -> decltype(
swap((T&&)t, (T&&)t)
);
template<typename T, typename U>
auto requires_(T && t, U && u) -> decltype(
(void)swap((T&&)t, (U&&)u),
(void)swap((U&&)u, (T&&)t)
);
}; If the So then struct Swappable
{
template<typename T>
auto requires_(T && t) -> decltype(
concepts::valid_expr(
(swap((T&&)t, (T&&)t), 42)
));
template<typename T, typename U>
auto requires_(T && t, U && u) -> decltype(
concepts::valid_expr(
(swap((T&&)t, (U&&)u), 42),
(swap((U&&)u, (T&&)t), 42)
));
}; Fail from forgetting the |
A more robust solution may look like this: template<typename T, typename U>
auto requires_(T && t, U && u) -> list<
decltype(swap((T&&)t, (U&&)u)),
decltype(swap((U&&)u, (T&&)t))
>; but a bit verbose, you probably want a macro for that. |
That is an awesome idea. It really is not anymore verbose than using struct Convertible
{
template<typename T, typename U>
auto requires_(T &&t, U &&) -> valid<
concepts::convertible_to<U>((T &&) t)
>;
}; I don't if substitution failure still applies in this context. Perhaps, another struct Convertible
{
template<typename T, typename U>
auto requires_(T &&t, U &&) -> decltype(valid<
concepts::convertible_to<U>((T &&) t)
>());
}; I will try to test this out later. |
Nm, |
@pfultz2 , I believe you mean it's needed for template<class... Ts>
using list = void; |
Yes. I was trying to find a way to avoid the extra struct EqualityComparable
{
template<typename T>
auto requires_(T && t) -> CONCEPT_VALID(
(concepts::convertible_to<bool>(t == t))
(concepts::convertible_to<bool>(t != t))
);
}; It would be nice to find a way to avoid the extra parens for at least the |
This code fails to compile: (tested clang 3.5 & g++ 4.9.1)
In the implementation,
concepts::valid_expr
is used, butswap
returns void, which cannot be an arg.Why is
concepts::valid_expr
needed at the first place?The text was updated successfully, but these errors were encountered: