【问题标题】:Member variable vs. function concept checking成员变量与函数概念检查
【发布时间】:2020-02-02 14:19:04
【问题描述】:

我沉迷于一些星期天早些时候的 C++20 恶作剧,在玩 gcc/clang 主干概念时,我偶然发现了一个我看不到优雅解决方案的问题。考虑这段代码:

template <typename T>
concept floating_point = std::is_floating_point_v<std::decay_t<T>>;

template <typename T>
concept indexable = requires(T v)
{
    {v[0]} -> floating_point;
    {v[1]} -> floating_point;
    {v[2]} -> floating_point;
};

template <typename T>
concept func_indexable = requires(T v)
{
    {v.x()} -> floating_point;
    {v.y()} -> floating_point;
    {v.z()} -> floating_point;
};

template <typename T>
concept name_indexable = requires(T v)
{
    {v.x} -> floating_point;
    {v.y} -> floating_point;
    {v.z} -> floating_point;
};

template <typename T>
concept only_name_indexable = name_indexable<T> && !indexable<T>;

template <typename T>
concept only_func_indexable = func_indexable<T> && !indexable<T> && !name_indexable<T>;

void test_indexable(indexable auto v) {
    std::cout << v[0] << " " << v[1] << " " << v[2] << "\n";
}

void test_name_indexable(only_name_indexable auto v) {
    std::cout << v.x << " " << v.y << " " << v.z << "\n";
}

void test_func_indexable(only_func_indexable auto v) {
    std::cout << v.x() << " " << v.y() << " " << v.z() << "\n";
}

(obligatory godbolt for toying with this)https://godbolt.org/z/gyCAQn

现在考虑一个满足only_func_indexable 的结构/类:拥有x()y()z() 的成员函数会立即导致name_indexable 的概念检查中出现编译错误。更准确地说:

<source>: In instantiation of 'void test_func_indexable(auto:3) [with auto:3 = func_point]':

<source>:125:26:   required from here

<source>:29:6: error: 'decltype' cannot resolve address of overloaded function

   29 |     {v.x} -> floating_point;

这有点明显,因为.x 指的是成员函数的名称,它是decltype 中的非法表达式。另请注意,将name_indexable 的定义更改为

template <typename T>
concept name_indexable = !func_indexable<T> && requires(T v)
{
    {v.x} -> floating_point;
    {v.y} -> floating_point;
    {v.z} -> floating_point;
};

通过惰性联合求值解决问题。

此时我的收获是:“每当我想检查一个成员变量的存在时,我必须首先提供并检查一个类似名称的成员不存在的概念函数”。

现在这感觉相当尴尬,就像 ISO 小组中的优秀人员想到了一个更优雅的解决方案一样。

在这种情况下该解决方案是什么?

最好, 理查德

【问题讨论】:

  • 如果一个类型有一个名为x 的成员函数,它不应该也有一个同名的成员变量。那么为什么你需要同时检查成员函数的存在和成员变量的不存在呢?
  • 不确定它是否优雅,但您可以使用嵌套需求并执行类似requires(floating_point&lt;decltype(a.x)&gt;) 而不是{v.x} -&gt; floating_point; 之类的操作。
  • 两者都没有必要存在,只需检查 only 具有成员函数的类型会导致类型错误(请参阅链接的godbolt)。考虑到这是非法的并且是实际错误,使用 decltype(a.x) 的解决方案也不应该起作用。
  • @RichardVock 我的意思是说v.x 而不是a.x 它也应该像v 一样工作,这取决于template context 内部requires 中变量的模板类型和编译错误@ false,还有{v.x} -&gt; floating_point 似乎在clang 主干中编译,所以我不确定是GCC 本身还是clang 的问题
  • @Gaurav Dhiman 据我了解,requires(floating_point&lt;decltype(v.x)&gt;){v.x} -&gt; floating_point; 编译的内容,所以我不认为这样做有什么意义。你是完全正确的,虽然它确实在铿锵声中编译。奇怪的。如果编译错误实际上被忽略为假(如 SFINAE),那么整个问题确实会归结为编译器错误:)。

标签: c++ c++-concepts


【解决方案1】:

我不确定您的问题是否真的是个问题。希望您永远不会有一个同时具有数据成员x 和成员函数x 的类型,因此您的func_indexable 已经是only_func_indexable,并且有了这个概念,就没有问题了。

但是如果你想在那里非常精确,你可以做一些这样的事情

requires std::is_member_object_pointer_v<decltype(&T::x)> && floating_point<std::invoke_result_t<decltype(&T::x), T>>;

当然,这应该包含在一些概念中,而不是每次都写。请注意,std::invoke_result_t&lt;decltype(&amp;T::x), T&gt;float x;float x(); 提供(一些参考)float

【讨论】:

  • 如我在上面的评论中所述,无需同时定义两者以触发编译错误。但是std::is_member_object_pointer 是一个很好的提示,实际上对我来说是新的。
猜你喜欢
  • 1970-01-01
  • 2018-04-25
  • 2022-12-14
  • 2012-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
相关资源
最近更新 更多