【问题标题】:Construction of a void Type?构造一个 void 类型?
【发布时间】:2016-10-08 15:39:05
【问题描述】:

我得到了一段使用void() 作为参数的代码。代码无法编译……显然?

我们可以实例化void 类型的任何东西吗?我相信答案是否定的,除了void*。例如:

  1. 写函数void askVoid(void param) {}错误:

参数可能没有void 类型

  1. 编写函数 void askNaught() {} 并使用 askNaught(void())` 错误调用它:

错误 C2660:takeNaught:函数不接受 1 个参数

  1. 编写模板化函数template <typename T> void takeGeneric(T param) {} 并使用takeGeneric(void()) 错误调用它:

错误 C2893:无法专门化函数模板 void takeGeneric(T)

  1. 声明void voidType 错误:

不允许不完整的类型

  1. 声明auto autoVoid = void() 错误:

无法推断auto类型

  1. 声明void* voidPtr 工作正常,但remove_pointer_t<decltype(voidPtr)> decltypeVoid 错误:

错误C2182:decltypeVoid:非法使用类型void

就是这样,对吧? C++ 中没有void() 的位置吗?这只是我得到的错误代码,对吧?

【问题讨论】:

  • 您可以在返回void的函数中返回void函数调用:void a() {}; void b() { return a(); }。这是一种 void 类型的表达式。
  • @rodrigo 在这种情况下a() 会被执行吗?这很奇怪,但我的编译器没有抱怨。类似于只调用a() 作为b() 的最后一行我猜。
  • 是的,和只是调用函数然后返回是一样的。简化模板的编写是 C++ 类型系统中的一个特殊规则:template<typename T> T foo() { return bar<T>(); }。否则,您将需要模板专业化,以便在 T=void 时删除虚假的 returns。
  • @rodrigo 太棒了,当你把它放在那个上下文中时,我看到能够用void 调用return 的重要性。我建议您将其放入答案中,但它看起来像 Black got there first,尽管它实际上并没有使用我所询问的类型。
  • 实际上,写@Black 的答案只是涵盖了原始问题,IMO。如果他愿意,我邀请他从我的评论中复制模板内容。

标签: c++ void


【解决方案1】:

C++(我说的是 C++,而不是 C)允许 (§6.6.3 comma 2) 具有 void 返回类型的函数返回 void 表达式,即:

void foo() { return void(); }

但请注意,它不是构造一个临时的void

【讨论】:

  • 令人着迷。所以这相当于return 对吧?
  • @JonathanMee 是的,没错。 @rodrigo 给出了这种用法的直接后果,即当您调用返回 void 的函数时。
  • 是的,我的印象是,这实际上是使用 this 来压缩关闭函数调用(@rodrigo 的 a())和从返回 void 的函数中提前返回到一个复合语句中。
  • 这在泛型代码中被大量使用以避免特殊情况 void 返回您“通过”其结果的函数。
【解决方案2】:

表达式void()void 类型的纯右值,可以在任何可能使用此类表达式的地方使用,[basic.fundamental]/9 提供了一个列表:

  • 作为表达式语句:void();
  • 作为条件运算符的第二个或第三个操作数:true ? throw 1 : void()
  • 作为逗号运算符的操作数:++it1, void(), ++it2
  • 作为decltypenoexcept的操作数:using my_void = decltype(void()); static_assert(noexcept(void()), "WAT");
  • 在函数的 return 语句中返回(可能是 cv 限定)void: const void f() { return void(); }
  • 作为显式转换为(可能是 cv 限定的)void 的操作数:static_cast<const void>(void())

void 类型的表达式也可以用作typeid 的操作数,但在这种情况下,void() 特别会被解析为类型,而不是表达式。

【讨论】:

    【解决方案3】:

    您可以将void() 作为函数参数:

    void test(void()) { ... }
    

    扩展为:

    void test(void (*)())
    

    这是一个函数指针,指向一个返回 void 且不带参数的方法。

    完整示例:

    void abc() {}
    void test(void()) { }
    
    int main() {
        test(abc);
    }
    

    【讨论】:

    • 不,如果您使用test(void),您表示的是一个不带参数的方法(在 C 中是必需的) - 还是我错了?
    • 你说得对,我意识到我的评论离题并删除了它,在问题解决后随时删除我们的两个 cmet
    • @sokkyoku 我认为没有必要删除它,它也可以帮助其他人澄清为什么大括号很重要:)
    • @JonathanMee: void(*)() 是一个函数指针类型void() 是一个函数类型。两者是相关的,但是not the same.
    • @interjay 问题以“我得到了一段使用 void() 作为参数的代码”开头,所以我认为这仍然是相关的
    【解决方案4】:

    您可以使用void() 作为可调用类型,例如std::function<void()> f; 是一个有效的语句。

    另外,来自8.3.5/4

    由一个非依赖类型 void 的未命名参数组成的参数列表等效于一个空参数列表。

    这意味着这是有效的:

    template<typename T>
    struct F;
    
    template<typename R, typename... A>
    struct F<R(A...)> { };
    
    int main () {
       F<void(void)> s;
    }
    

    在这里,您没有实例化 void 类型的任何内容,而是将它(让我说)用作可调用类型的参数列表。

    不确定这是否回答了你的问题,我不清楚这个问题到底是什么。

    【讨论】:

    • 除非我看错了,否则您将 void 用作 type 而不是 constructing void 的任何内容,对吗?
    【解决方案5】:

    C++中没有void()的地方吗?

    作为表达式,void() 在 C++ 中有效。

    来自标准,$5.2.3/2 Explicit type conversion (functional notation) [expr.type.conv]

    表达式T(),其中T 是简单类型说明符或 typename-specifier 用于非数组完整对象类型或 (可能是 cv 限定的)void 类型,创建指定的纯右值 类型,其值是由值初始化 (8.5) 产生的 T 类型的对象; void() 的情况没有进行初始化。

    来自cppreference.com

    new_type ( )

    如果new_type是对象类型,则对象是值初始化的; 否则,不进行初始化。如果new_type 是(可能 cv-qualified)void,表达式是void prvalue。

    【讨论】:

    • 我从这里读到:en.cppreference.com/w/cpp/language/explicit_cast 我明白你在说什么,但我看不出它在哪里有效。我的意思是我在我能想到的所有情况下都尝试了void()
    • @JonathanMee 您尝试过什么上下文?顺便说一句:T.C.的答案提供了一个列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 2016-12-27
    • 1970-01-01
    • 2020-09-18
    • 1970-01-01
    • 2014-09-17
    相关资源
    最近更新 更多