函数指针是否在调用时使用

Pas*_* By 14 c++ language-lawyer

这个问题是由意见引起这里

请考虑以下代码

template <typename T, typename C>
void g(T, C) {}

template <typename T, typename C>
struct G
{ 
    static constexpr void (*m) (T, C) = &g; 
}; 

void foo()
{
    auto l = [](int){return 42;};
    G<int, decltype(l)>::m(420, l);
}
Run Code Online (Sandbox Code Playgroud)

这在C++ 17中是合法的,通过内联变量和所有这些G::m来定义G.

有什么奇怪的是在C++ 14和C++ 11 gcc拒绝这个说明m被使用但从未定义,而clang接受它.生活

是否使用了modr?或者这是一个gcc bug?

Pas*_* By -1

TLDRm没有被 odr 使用,这确实是一个 GCC 错误。

网上争议解决使用

直观上,变量需要存储在内存中的某个地方。唯一的例外是编译器可以优化变量的值并且永远不会以其他方式使用变量的值。Odr-usage 形式化了这个想法:变量仅在使用 odr 时才需要定义。

Odr 用法由[basic.def.odr]定义

名称x显示为潜在计算表达式的变量ex将被 odr 使用,ex除非应用左值到右值转换来x生成不调用任何非平凡函数的常量表达式,并且如果x是对象,ex则为以下元素的元素表达式 的潜在结果集e,其中左值到右值转换应用于e,或者e是丢弃值表达式。

换句话说,x如果满足以下任一条件,则不会使用 odr

  1. x不显示为潜在评估的表达式(例如decltype(x))。
  2. x不是对象(例如引用)并且应用左值到右值转换来x生成常量表达式。
  3. x是一个对象,应用左值到右值转换来x生成常量表达式。此外,还有一个“耦合”表达式e,它要么应用左值到右值转换,要么被丢弃。

“耦合”是指直觉上存在某种与之密切相关的封闭表达式,x该表达式只能以某种方式使用,而无需x存储在内存中。这个概念通过潜在结果的定义来形式化[basic.def.odr]

表达式的潜在结果集e定义如下:

  • 如果e是一个 id 表达式,则该集合仅包含 e。

  • 如果e是带有数组操作数的下标运算,则该集合包含该操作数的潜在结果。

  • 如果e是类成员访问表达式,则该集合包含对象表达式的潜在结果。

  • 如果e是一个指向成员表达式,其第二个操作数是常量表达式,则该集合包含对象表达式的潜在结果。

  • 如果e有形式(e1),则该集合包含 的潜在结果e1

  • 如果e是泛左值条件表达式,则该集合是第二个和第三个操作数的潜在结果集合的并集。

  • 如果e是逗号表达式,则该集合包含右操作数的潜在结果。

  • 否则,该集合为空。

ex位于 的潜在结果范围内的表达式e与 ”耦合” e

问题

应用定义

  1. m可能会被评估。
  2. 应用左值到右值转换来m生成常量表达式。
  3. m是一个对象。
  4. m是一个表达式,其潜在结果包括m并应用了左值到右值转换。

因此我们得出的结论m是没有使用过odr。