While compiling the following (reduced) code:
#include <tuple>
#include <stdlib.h>
template<size_t N> struct tying;
template<> struct tying<1> {static auto function(auto& o) -> decltype(auto) {auto& [p1] = o;return std::tie(p1);}};
template<> struct tying<2> {static auto function(auto& o) -> decltype(auto) {auto& [p1,p2] = o;return std::tie(p1,p2);}};
template<typename T, size_t N> concept bool n_components =
requires(T& object) {
{ tying<N>::function(object) };
};
typedef struct
{
int a;
float b;
} test_t;
int main(int argc, char* argv[])
{
constexpr size_t n = 1;
constexpr bool t = n_components<test_t, n>;
printf("n_components<test_t, %d>: %s\n", n, t ? "yes" : "nope");
return 0;
}
with gcc specific options -std=c++1z -fconcepts
the gcc (version 7.3.0, and x86-64 gcc-trunk at godbolt also) fails with an error:
error: only 1 name provided for structured binding
note: while 'test_t' decomposes into 2 elements
I was very surprised by this, as the error "raises" inside the requires-expression
, and to my understanding, this should result in n_components
constraint to get evaluated to false, as the requirement is not satisfied.
Note: replacing constexpr size_t n = 1;
with other values "fixes" the error, so I presume the n_components
constraint is not to blame:
n = 2
the n_components
constraint evaluates to "true" as expected.n = 3
the n_components
constraint evaluates to "false" as expected - due to referring to unspecialized tying<3>
struct.It seems there are no other compilers supporting the c++ concepts and structured bindings available yet to compare with.
PS. I was playing around with "poor man's reflection" a.k.a. magic_get
and hoped to replace the unreliable is_braces_constructible
trait with something more robust...
Here's a partial solution for clang since 5.0.
It is distilled from a recent reddit post
Find the number of structured bindings for any struct
The method is non-standard, using gnu extension statement expressions to effectively SFINAE on structured binding declarations. It is also non-portable as gcc fails to parse structured binding in statement expression in a lambda trailing return type.
template <typename... Ts> struct overloads : Ts... { using Ts::operator()...; };
template <typename... Ts> overloads(Ts...) -> overloads<Ts...>;
template <typename T>
auto num_bindings_impl() noexcept {
return overloads{
[](auto&& u, int) -> decltype(({auto&& [x0] = u; char{};}))(*)[1] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1] = u; char{};}))(*)[2] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1,x2] = u; char{};}))(*)[3] {return {};},
[](auto&& u, int) -> decltype(({auto&& [x0,x1,x2,x3] = u; char{};}))(*)[4] {return {};},
[](auto&& u, unsigned) -> void {}
}(declval<T>(), int{});
};
This implementation to be used only in unevaluated context and expanding out
for as many bindings as you wish, up to compiler implementation limits.
Then, you can define traits that work up to that limit:
template <typename T>
inline constexpr bool has_bindings = [] {
if constexpr ( ! is_empty_v<T> )
return ! is_void_v< decltype(num_bindings_impl<T>()) >;
else return false;
}();
template <typename T>
inline constexpr unsigned num_members = [] {
if constexpr ( ! is_empty_v<T> )
return sizeof *num_bindings_impl<T>();
else return 0;
}();
https://godbolt.org/z/EVnbqj
I was very surprised by this, as the error "raises" inside the requires-expression
To be precise, the error happens in the body of the function
s you wrote. We can boil down your code to:
void function(auto& arg);
void function_with_body(auto& arg)
{
arg.inexistent_member;
}
template<typename Arg>
concept bool test = requires(Arg arg) { function(arg); };
// never fires
static_assert( test<int> );
template<typename Arg>
concept bool test_with_body = requires(Arg arg) { function_with_body(arg); };
// never fires
static_assert( test_with_body<int> );
(on Coliru)
As far as the requires
expression are concerned, the function calls are valid C++ expressions: there's nothing tricky about their return and parameter types, and the supplied argument can be passed just fine. No check is performed for the function bodies, and with good reason: the function or function template may have been declared but not defined yet (i.e. as is the case for function
). Instead, it's normally up to the function template writer to make sure that the function body will be error-free for all (sensible) specializations.
Your code uses return type deduction, but that won't make too much of a difference: you can declare a function with a placeholder type (e.g. decltype(auto)
) without defining it, too. (Although a call to such a function is in fact an invalid expression since the type cannot be deduced and that's visible to a requires
expression, but that's not what you're doing.) In pre-concepts parlance, return type deduction is not SFINAE-friendly.
As to your wider problem of writing a constraints around structured bindings, you are out of luck. As best as I know these are all the obstacles:
std::tuple_size
is for tuple-like types only.). This could be worked around by blindingly trying sizes in increasing order though.requires
expression.The closest you can go is emulate what the language is doing during an actual structured binding. That can get you support for tuple-like types (including arrays), but not 'dumb' aggregates.
(range-for is another case of a statement-level constraint that you can't fit into an expression constraint, and where emulating the language can only get you so far.)
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