【问题标题】:Why isn't "this" known at compile time in a consteval constructor?为什么在 consteval 构造函数中编译时不知道“this”?
【发布时间】:2020-06-04 00:48:15
【问题描述】:

我正在编写一个类来包装与设备一起使用的基于 C 的库的一部分,其中每个设备都配置有用于处理数据的回调函数指针。将为每个设备创建一个MyClass 的实例。见下文:

struct DeviceConfig {
    void (*callback)(char *data);
};

class MyClass {
private:
    DeviceConfig config;

public:
    void myCallback(char *data);

    MyClass() {
        // Would like to set config.callback so that a call to it will result in a call of this->myCallback(data).
    }
};

由于无法将捕获的 lambda 转换为函数指针,因此我尝试了以下解决方法:

template<MyClass *MC>
auto binder() {
    return [](char *data) { MC->myCallback(data); };
}

MyClass::MyClass() {
    config.callback = binder<this>();
}

但是,编译器(GCC 最新)不喜欢在构造函数中使用 binder,因为 this 在编译时不一定知道,尽管我知道 MyClass 的实例只会在编译时声明。

C++20 引入了consteval 函数(和构造函数),其中"must produce a compile-time constant."。但是,将consteval 添加到构造函数和/或binder 不会影响编译器的输出。 constexpr 也没有改变。

如果一个对象可以在编译时初始化,为什么对象的this也不能在编译时知道呢?以上可以通过其他方式实现吗?

【问题讨论】:

  • this 实际上是成员函数的参数。
  • @NicolBolas 这似乎是正确的副本,但我认为您应该在此处添加一个答案,解释为什么即使 consteval 函数也不允许这样做。
  • @NicolBolas 这是我的参考资料,但您可能有不同的参考资料:groups.google.com/a/isocpp.org/d/msg/std-proposals/FvE9w5_onQo/…
  • 在持续评估期间实例化模板变得很混乱。
  • @NicolBolas 我不明白这个问题是如何回答我的,它根本不处理类或对象。它当然不能回答为什么 this 在 consteval 构造函数中不可用。

标签: c++ c++20


【解决方案1】:

构造函数是函数,就像其他函数一样。与其他函数相比,它们的特权很少,其参数的常量表达式行为不是其中之一。

this 本质上是所有非静态成员函数的参数。参数为never 常量expressions。因此,this 不能在需要常量表达式的上下文中使用。如何创建类实例并不重要。不管你怎么称呼它。 constexpr/consteval 函数的参数绝不是常量表达式,包括 this

【讨论】:

  • 我明白了。但是,我认为我正在寻找的功能应该在constexpr/consteval 上下文中是可能的。我在编译器资源管理器上整理了一个example;看来编译器可以知道实现我正在寻找的东西需要什么,只是功能不存在。
  • @clyne:什么“应该可能”是无关紧要的;目前语言不允许。有一些建议允许使用 constexpr 参数(大概包括 this),但这些建议是在 C++23(充其量)时间范围内,因此不代表 C++20。
  • 我想知道为什么这个功能是不可能的;使这成为可能的建议很重要,很高兴看到它们包含在您的答案中。说这是不可能的,然后把它留在那读起来很像“这永远不可能”,但事实似乎并非如此。
  • @clyne:但这不是你问的问题。你问为什么你写的代码不起作用。而且它不起作用,因为参数不是 C++ 中的常量表达式。询问是否有可能修改 C++ 以使参数能够成为常量表达式是一个完全不同的问题。并且超出了 Stack Overflow 的范围。
  • @NicolBolas:虽然要求对尚未做出的决定进行预测是不合适的,但询问扩展语言可能存在哪些技术障碍是不合适的,并且可能具有教育意义。作为一个相关示例,考虑到即使 consteval 函数也必须具有 one 返回类型,这对于理解其参数缺少常量表达式状态非常有帮助。
【解决方案2】:

接受的答案确实解释了为什么 this 不能在构造函数中使用;但是,我还询问是否有解决方法来实现我想要的。我找到了一种解决方法,并在另一个 StackOverflow 帖子 here 中分享了该工作。

为了节省点击,这是我的代码的最后一次迭代:

struct DeviceConfig {
    void (*callback)(const char *);
};

template<auto& v, auto f>
constexpr auto member_callback = [](auto... args) { (v.*f)(args...); };

class MyClass {
private:
    DeviceConfig config;

public:
    consteval MyClass(const DeviceConfig& cfg = {}) :
        config(cfg) {}

    template<MyClass& MC>
    constexpr static MyClass& init() {
        MC.config.callback = member_callback<MC, &MyClass::myCallback>;
        return MC;
    }

    void myCallback(const char *data);
};

int main()
{
    constinit static MyClass mc = (mc = MyClass(), MyClass::init<mc>());
}

【讨论】:

    猜你喜欢
    • 2012-07-14
    • 2011-06-21
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2019-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多