模板成员函数和std::Invoocable的C++20概念中存在错误

2022-05-10 00:00:00 c++ concept eigen c++20 invocable

我正在试验C++20概念和Eigen library,我遇到了意外的行为。具体地说,请考虑以下概念,该概念要求类型可以通过Eigen::Matrix<double, -1, 1>>对象或Eigen::Matrix<char, -1, 1>>对象调用:

template <class FOO_CONCEPT>
concept FooConcept = std::invocable<FOO_CONCEPT, Eigen::Matrix<double, -1, 1>> &&
    std::invocable<FOO_CONCEPT, Eigen::Matrix<char, -1, 1>>;

然后,查看以下结构中的注释行(*):

struct Foo {
    // template <typename T>    <----    (*)
    void operator()(Eigen::Matrix<double, -1, 1>) {
    }

    void operator()(Eigen::Matrix<float, -1, 1>) {
    }
};
请注意,类Foo不满足FooConcept的要求,因为不能使用Eigen::Matrix<char, -1, 1>参数调用它。确实:

std::cout << FooConcept<Foo> << std::endl;

打印0。然而,当我切换行注释(*)时,即operator()是模板时,相同的代码奇怪地打印1。这是个窃听器吗?我使用Clang 12.0.1和GCC 11.1.0在Visual Studio Code上编译代码,都得到了这些结果。感谢您能提供的任何帮助!

附注:行

 std::cout << std::is_convertible<Eigen::Matrix<char, -1, 1>, Eigen::Matrix<float, -1, 1>>()
              << std::endl;
打印1,但Eigen::Matrix<char, -1, 1>对象不能隐式转换为Eigen::Matrix<float, -1, 1>。这是另一个窃听器吗?这是否与上述问题有某种关联?


编辑1:我注意到通过定义

struct FooImplicit {
    void operator()(Eigen::Matrix<char, -1, 1>) {
    }
};

FooImplicit结构实际上满足FooConcept,如果将char替换为double,也会满足char。这看起来与两种特征类型的可转换性有关--请参阅P.S.

如何在不允许隐式转换的情况下表达所需的约束?即FooConcept必须只允许重载operator()至少两次,一次重载Eigen::Matrix<double, -1, 1>,一次重载Eigen::Matrix<char, -1, 1>。可以这样做吗?

另外,如果我定义函数

void func(FooConcept auto x) {}

我尝试将其称为func(Foo());保持行(*)的注释,得到以下编译错误:

[build] [...]: note: because 'Foo' does not satisfy 'FooConcept'
[build] void func(FooConcept auto x) {
[build]               ^
[build] [...]: note: because 'std::invocable<Foo, Eigen::Matrix<char, -1, 1> >' evaluated to false
这是因为编译器不能明确地选择调用哪个重载吗?如果是,为什么错误消息没有更明确?在我看来,就像编译器注意到Foo有两个成员函数,一个是正确的,而另一个是不正确的。


编辑2:我设法回答了this post中问题的一半。但是,我仍然对从编译器收到的错误消息感到好奇。


解决方案

is_convertable只是确定过载是否存在并且可以明确地找到。在Eigen的情况下,存在这些类型之间的重载转换,但主体具有static_assert

is_convertable在说转换操作有效之前,不会实例化转换操作体。这是有意的,以允许C++重载解析不需要编译大量代码。

要使您的特征起作用,需要重新设置Eigen以支持&Quot;SFINAE&Quot;友好的方法和转换操作符。

测试中的失败是因为char矩阵转换为两者,这是不明确的,因此特征失败。这不是您认为它失败的原因。

添加template<class T>表示不考虑重载(无法推导T),因此明确选择转换为浮点数组。

相关文章