3

给出一些声明:

template <class T, T t>
struct foo {};

template <class T>
constexpr T ident(T t) {
   return t;
}

constexpr int bar() {
   return 0;
}

int main(int argc, const char *argv[])
{
    foo<bool, true> a;
    foo<int, bar()> b;
    foo<int, ident(0)> c;
    foo<int (*)(), bar> d;

    foo<int(*)(), ident(&bar)> e; // not accepted (gcc 4.7.2 crashes here, even)

    return 0;
}

另外:有趣的是,这导致了 gcc 4.7.2 的段错误。我不得不通过我的 4.8.0 快照的 svn 构建运行它,甚至得到一条错误消息(“必须是具有外部链接的函数地址”)......

为什么第一个是好的,最后一个是不允许的——这个 constexpr 不像 case ad 吗?似乎编译器完全有能力确定ident(&bar)正在谈论的函数,因为它可以为其他类型做到这一点。

4

2 回答 2

2

从 C++17 开始,这将被允许;请参阅N4198(以及最近被投票纳入 C++ 标准的相应措辞文件N4268)。Clang trunk 在-std=c++1z模式下接受您的代码。

于 2014-12-16T03:36:14.927 回答
1

E 的问题在于,对于非类型模板参数,类型为指向函数的指针,获取它的地址必须是有效的(14.3.2)。例如,foo<int(*)(), &ident(bar)>无效。因此,即使返回的ident(bar)是指向具有有效外部链接的函数的指针,对于非类型模板参数,整个表达式也是无效的。如果您从 中返回 0(或 nullptr)ident(bar),它将被编译(也在 14.3.2 中定义)。

该标准允许您在一种函数指针类型上省略 &,但它仍然必须有效才能获取它的地址。这就是为什么foo<int (*)(), bar>有效,因为foo<int (*)(), &bar>它是有效的。另一个函数要求 B、C(和 A)计算为整数常量,它属于不同的类别。

于 2013-04-07T19:49:46.037 回答