概念类包含具有精确枚举函数参数的函数c++20

r7knjye2  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(134)

我试图创建一个概念,缩小什么样的类可以与预定义的函数类型一起使用。我设法弄清楚如何将概念用于返回类型,但我仍然在努力不允许函数参数的隐式转换。特别是使用枚举。枚举定义为:

typedef enum GpioLevel {
    LOW, HIGH
} GpioLevel;

字符串
目前,我的(有点混乱的)概念看起来如下:

template <typename P>
concept pinConcept = requires (GpioLevel level) {
  P::Write(level);
  requires std::same_as<std::underlying_type_t<decltype(level)>,std::underlying_type_t<GpioLevel>>;
  P::Write(std::declval<GpioLevel>());
  {P::GetLevel()} -> std::same_as<GpioLevel>;
};


那么一个应该遵循这个概念的类应该是:

class PinTemplate {
public:
    static inline void Write(double level) {
        std::cout<<"Writing "<<std::to_string(level) <<std::endl;
    }
    static inline GpioLevel GetLevel() {
        return GpioLevel::HIGH;
    }
};


这不应该被我的概念所接受,因为Write函数使用double而不是GpioLevel。然而,我运行得很好,有许多错误的数据:

template <pinConcept pin>
class SpiPinPort {
public:
    void WriteData(){
        pin::Write(GpioLevel::LOW);
        // Below should actually not be allowed, but somehow it just casts it
        pin::Write(5);
        pin::Write('a');
    }
};

int main()
{
    SpiPinPort<PinTemplate> port;
    port.WriteData();
}


这给了我一个输出:

  • 写0.000000*
  • 写5.000000*
  • 写作97.000000*

然而,我希望在SpiPinPort示例化时出现编译错误,因为PinTemplate不遵守pinConcept概念。如果我将PinTemplate类的签名从double更改为GpioLevel,它确实会抛出一些错误(正如我所期望和希望的那样)。但我想限制允许模板类使用错误函数模板的可能性。
https://godbolt.org/z/v587aqcco
谢谢你

9q78igpj

9q78igpj1#

首先,让我们真实的浏览一下您的概念:

template <typename P>
concept pinConcept = requires (GpioLevel level) {
  P::Write(level);
  requires std::same_as<std::underlying_type_t<decltype(level)>,std::underlying_type_t<GpioLevel>>;
  P::Write(std::declval<GpioLevel>());
  {P::GetLevel()} -> std::same_as<GpioLevel>;
};

字符串
第二个要求:

requires std::same_as<std::underlying_type_t<decltype(level)>,std::underlying_type_t<GpioLevel>>;


decltype(level)GpioLevel,所以这是检查相同类型的两个不同拼写是否相同。当然,它们是相同的。这个要求实际上没有做任何事情。
第三个要求:

P::Write(std::declval<GpioLevel>());


你很少需要在概念中使用declval,因为requires表达式允许你引入参数。在这种情况下,你已经有了一个GpioLevel--level。这已经是你在第一个需求中检查的内容。
现在,有一个区别:P::Write(level)检查你是否可以用左值GpioLevel调用WriteP::Write(std::declval<GpioLevel>())检查你是否可以用x值GpioLevel调用Write。但我真的怀疑这就是你要做的,或者这个区别在这里很重要。
所以整件事可以是:

template <typename P>
concept pinConcept = requires (GpioLevel level) {
  P::Write(level);
  {P::GetLevel()} -> std::same_as<GpioLevel>;
};


也就是说,P::Write(level)的需求并没有检查任何关于 types 的东西,而是检查 expression。你能用GpioLevel调用P::Write吗?
现在,GpioLevel是一个无作用域的枚举。这意味着这是完全正确的:

int main() {
    // some function taking a double
    auto f = [](double){};

    f(LOW); // ok
}


这是可以的,所以PinTemplate::Write(LOW)(尽管Write取了double)也是可以的,所以PinTemplate满足pinConcept
解决这个问题的最好方法是将GpioLevel更改为scoped enum:

enum class GpioLevel { LOW, HIGH }


现在GpioLevel不能转换为double,所以PinTemplate不再满足pinConcept
如果这不是一个选项,那么你唯一的其他方法是检查P::Write是一个具有特定类型的静态函数:

requires std::same_as<decltype(P::Write), void(GpioLevel)>;


这是更多的限制,因为现在P::Write不能是函数模板或具有默认函数参数。

相关问题