C++中将void指针用作模板参数

11

以下代码无法编译:

template<void *p>
class X {
// ...
};

int r;

int main()
{
    X<&r> x;

    return 0;
}

错误信息为:

x.cc:10:6: error: could not convert template argument ‘& r’ to ‘void*’

将&r显式地转换为(void *)也没有帮助。错误信息变成了:

x.cc:10:14: error: could not convert template argument ‘(void*)(& r)’ to ‘void*’

标准的哪个部分说明了这种行为?GCC版本为gcc version 5.2.1 20151003 (Ubuntu 5.2.1-21ubuntu2)

编辑:

请注意,使用例如int *而不是void *会按预期工作。

编辑:(回答自己)

指定-std=c++1z时,使用gcc HEAD 6.0.0 20151016 (experimental)无论是隐式还是显式强制转换为"void *"都不能正常工作。 当不进行显式转换时,clang会报出以下错误:

x.cc:10:7: error: conversion from 'int *' to 'void *' is not allowed in a converted constant expression

负责修复C++语言规范中这个漏洞的是N4268,clang已经实现了。


我并不认为这个问题可以视作被提及的那个问题的副本。这个问题是关于使用"void *"作为模板参数,而另一个问题则是关于使用"const char *"作为模板参数。 - apriori
从技术上讲,[temp.param]/p4禁止使用void*作为模板非类型参数,尽管我所知道的主要编译器都没有执行此禁令,甚至没有发出诊断信息。 - T.C.
好的,很有趣。你指的是哪个文档? - apriori
2个回答

1
通常情况下,任何指针都可以转换为void*类型的指针。
[C++11, 4.10/2]类型为“指向cv T”的prvalue(其中T是对象类型)可以转换为类型为“指向cv void”的prvalue。将“指向cv T”的指针转换为“指向cv void”的结果指向存储T类型对象的起始位置,就像该对象是T类型的最终派生对象(1.8)一样(即不是基类子对象)。空指针值被转换为目标类型的空指针值。
然而,对于非类型模板参数,规定了特定的转换方式:
[C++11, 14.3.2/5] 对每个用作非类型模板参数的表达式执行以下转换。如果无法将非类型模板参数转换为相应模板参数的类型,则程序不合法。对于指向对象的非类型模板参数,应用限定转换(4.4)和数组到指针的转换(4.2)。如果模板参数的类型为std::nullptr_t,则应用空指针转换(4.10)。通过省略,我们可以推断出这种转换是不允许的。

1
我想不出任何原因导致这种不一致性。对我来说,这是一个错误。 - apriori

1
我不能立即引用具体的章节和节(欢迎编辑),但你试图做的在C++中是不允许的。
模板参数必须在编译时就已知。除非:
1. 在模板参数列表中使用“= nullptr”为默认值。 2. 它们是成员函数指针(它们仅仅是偏移量,因此在编译时就已知)。
例如,以下代码可以编译:
template<void * = nullptr>
class X {
    // ...
};

int r;

int main()
{
    X<nullptr> x;

    return 0;
}

1
不,将“void *”替换为“int *”,则代码示例可以编译。 - apriori
我只想补充一点,当与类型特征等一起使用时,语法并不容易理解。以下是一个使用std::enable_if的示例:template <typename T, typename std::enable_if<sizeof(T) == 4, T>::type* = nullptr> void do_if_ptrs_are_32bit(void * p) {} - Bim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接