编译以下(简化)代码时:
'/\\.js$/'
使用gcc特定选项#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;
}
gcc(版本7.3.0和godbolt上的x86-64 gcc-trunk)也失败并显示错误:
错误:只为结构化装订提供了1个名称。注意:while&#39; test_t&#39;分解为2个元素
我对此感到非常惊讶,因为错误&#34;提升&#34;在-std=c++1z -fconcepts
内,根据我的理解,这会导致requires-expression
约束被评估为false,因为不满足要求。
注意:将n_components
替换为其他值&#34;修复&#34;错误,所以我认为constexpr size_t n = 1;
约束不应该受到责备:
n_components
n = 2
约束评估为&#34; true&#34;正如所料。n_components
n = 3
约束评估为&#34; false&#34;正如预期的那样 - 由于引用了非专业化的n_components
结构。似乎没有其他支持c ++概念和结构化绑定的编译器可供比较。
PS。我正在玩着#34;穷人的反思&#34; a.k.a. tying<3>
并希望用更强大的东西取代不可靠的magic_get
特质......
答案 0 :(得分:0)
我对此感到非常惊讶,因为错误“提升”在requires-expression
中
准确地说,错误发生在你写的function
的正文中。我们可以将您的代码归结为:
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> );
就requires
表达式而言,函数调用是有效的C ++表达式:它们的返回和参数类型没有什么棘手的,并且提供的参数可以很好地传递。不对函数体执行检查,并且有充分的理由:函数或函数模板可能已经声明但尚未定义(即function
的情况)。相反,通常由函数模板编写者来确保函数体对所有(合理的)特化都没有错误。
您的代码使用了返回类型推导,但这并没有太大的区别:您可以声明一个具有占位符类型的函数(例如decltype(auto)
)而不定义它。 (虽然对这样一个函数的调用实际上是一个无效的表达式,因为该类型不能被推导出来并且对于requires
表达式是可见的,但这不是你正在做的事情。)前概念说法,返回类型演绎不是SFINAE友好的。
关于围绕结构化绑定编写约束的更广泛问题,你运气不好。据我所知,这些都是障碍:
std::tuple_size
仅用于类似元组的类型。)。这可以通过盲目地尝试增加顺序的大小来解决。requires
表达式的主体中。您最接近的是模拟实际结构化绑定期间语言正在执行的操作。这可以让你支持类似元组的类型(包括数组),但不支持'哑'聚合。
(range-for是一个语句级约束的另一种情况,你无法将其置于表达式约束中,并且模拟语言只能让你到目前为止。)
答案 1 :(得分:0)
从5.0开始,这是clang的部分解决方案。
它是根据最近的Reddit帖子摘录的
Find the number of structured bindings for any struct
该方法是非标准的,使用gnu扩展语句表达式有效地对结构化绑定声明进行SFINAE。这也是不可移植的,因为gcc无法以lambda尾随返回类型解析语句表达式中的结构化绑定。
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{});
};
此实现只能在未评估的上下文中使用并扩展
尽可能多的绑定,直至编译器实现限制。
然后,您可以定义可以达到该限制的特征:
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;
}();