g++ 和 clang++ 具有指向可变参数模板函数的指针的不同行为

g++ and clang++ different behaviour with pointer to variadic template functions

C++ 标准专家的另一个 "who's right between g++ and clang++ ?" 问题。

代码如下

template <typename ...>
struct bar
 { };

template <typename ... Ts>
void foo (bar<Ts...> const &)
 { }

int main ()
 {
   foo<int>(bar<int, long>{});     // g++ and clang++ compile

   (*(&foo<int>))(bar<int, long>{});  // g++ and clang++ give error

   (&foo<int>)(bar<int, long>{});  // clang++ compiles; g++ gives error
 }

模板函数 foo() 接收可变模板参数 bar

第一次通话

   foo<int>(bar<int, long>{});     // g++ and clang++ compile

适用于 clang++ 和 g++。

如果我理解正确的话,foo<int> 被解释为 只有 第一个模板参数并且这没有完成 Ts... 参数列表。所以编译器查看参数(一个 bar<int, long> 对象)并推导出完整列表。

第二次调用不同

     (*(&foo<int>))(bar<int, long>{});  // g++ and clang++ give error

如果我理解正确的话,使用 (&foo<int>) 我们得到指向 foo 实例化的指针,其中 Ts... 正好是 int(不仅是第一种列出但整个列表)并取消引用它(*(&foo<int>))并使用错误的参数调用它(bar<int, long> 对象)我们得到(clang++ 和 g++)编译错误。

到目前为止,还不错。

第三次调用出现问题

   (&foo<int>)(bar<int, long>{});  // clang++ compiles; g++ gives error

我确信(也许我错了)第二个(我们修复了 Ts... 中的所有模板类型,然后我们用错误的参数调用函数)但是 g++ 似乎同意(并给出错误) 其中 clang++ 不同意(并且编译没有问题)。

像往常一样,问题是:谁是对的?

让我们来看一个更简单的案例:

template <class A, class B>void foo(B) {};

int main()
{
    (&foo<char>)(1);
}

clang++ 编译它,而 g++ 失败并显示以下消息:

error: address of overloaded function with no contextual type information

同样的消息是针对例如这个程序:

void moo(int) {};
void moo(int*){};

int main()
{
    &moo != nullptr;
}

显然,其意图是参考 [over.over],其中讨论了获取重载函数的地址。标准中的这个地方指定了在什么上下文中可以使用重载函数名称(赋值的 RHS,函数调用中的参数等)。然而它说

An overloaded function name shall not be used without arguments in contexts other than those listed. (emphasis mine)

现在,(&foo<char>)(1) 中的 foo 是否使用了 无参数 ? g++ 好像就这么沉了。但是它很高兴编译

(&moo)(1);

来自第二个例子。所以我们在这里至少有一些不一致。获取函数模板和重载集的地址的规则相同,因此 (&foo<char>)(1)(&moo)(1) 应该都有效或都无效。该标准本身似乎表明嘿应该都是有效的:

The overloaded function name can be preceded by the & operator [...] [Note: Any redundant set of parentheses surrounding the overloaded function name is ignored (5.1). — end note]