模板参数类型 `void` 与显式使用 `void`

Ric*_*ten 19 c++

下面的代码中,为什么模板函数的显式扩展foo无法编译,而 的扩展却bar编译成功?实时链接 - https://godbolt.org/z/o8Ea49KEb

\n
template <typename T1, typename T2>\nT1 foo(T2) { T2(42); return T1{}; };\n\ntemplate <typename T1, typename T2>\nT1 bar(void) { T2(42); return T1{}; };\n\nint main()\n{\n    foo<int, void>();    // fails\n\n    bar<int, void>();    // works\n}\n
Run Code Online (Sandbox Code Playgroud)\n

T2请注意,两个函数的主体中都使用了模板参数,唯一的区别是函数参数 tobar已被手动替换。

\n

这个问题是在阅读std::conditional - Invalid argument type \xe2\x80\x98void\xe2\x80\x99 即使在测试 \'void\'并试图简化问题时受到启发的。

\n

asc*_*ler 24

(void)参数列表与空参数列表相同的规则可以在 C++ 标准[dcl.fct]/4()中找到:

由非依赖类型的单个未命名参数组成的参数列表void相当于空参数列表。除这种特殊情况外,参数不得具有cv void类型。

这个问题的重要部分是“非依赖类型”。模板参数T与类型相关,因此它不会触发规则。

我认为标准有这条规则,因为如果实例化恰好使该参数类型为通常采用一个参数的模板函数可能突然变成零参数函数,那么它也会非常混乱(我想说更糟)void。有了这条规则,我们就知道当一个模板用参数声明时,它确实有一个参数。

  • @ALX23z `int foo(void);` 不是旧的 C 风格 - 在现代 C 中推荐使用它,因为 `int foo();` 不是原型,并且允许任意数量的参数和/或参数,而无需进行一致性检查。但是,是的,这就是它在 C++ 中被允许的原因。 (7认同)

小智 5

考虑以下:

template<typename T>
void foo(T) {}

void bar(void) {}
Run Code Online (Sandbox Code Playgroud)

您假设foo<void>bar(void)是等价的,但它们不是。foo<T>总是需要争论。所以foo<void>相当于一个函数baz(void x)。这不会编译,因为您不能拥有 void 类型的值。这与您收到的错误消息相同。

在函数签名中,void bar(void)括号中的 void 表示不同的含义:没有参数的函数。这是C的残余,不推荐。

函数bar<void>(T) baz(void x)不能存在,因为它们都需要类型 的值void,而该值不能存在。

模板函数的解决方案是为模板实例化非法的情况提供重载或专门化。

template<typename T>
void foo(T) {} // one argument

void foo() {}  // no arguments -> different signatures -> overload

template<typename T> 
void bar() {}  // no arguments

template<>
void bar<void> {} // still no arguments -> same signature -> specialisation
Run Code Online (Sandbox Code Playgroud)