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

max*_*x66 12 c++ templates variadic-templates c++11 template-argument-deduction

另一个"g ++和clang ++之间谁是正确的?" C++标准大师的问题.

代码如下

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
 }
Run Code Online (Sandbox Code Playgroud)

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

第一个电话

   foo<int>(bar<int, long>{});     // g++ and clang++ compile
Run Code Online (Sandbox Code Playgroud)

适用于clang ++ ang g ++.

如果我理解正确,与foo<int>被阐明只有第一个模板参数,这没有完成的列表Ts...参数.因此编译器会查看参数(bar<int, long>对象)并推导出完整列表.

第二个电话是不同的

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

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

到现在为止还挺好.

问题出现在第三次电话会议上

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

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

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

n. *_* m. 3

让我们看一个更简单的情况:

\n\n
template <class A, class B>void foo(B) {};\n\nint main()\n{\n    (&foo<char>)(1);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
\n

错误:重载函数的地址没有上下文类型信息

\n
\n\n

例如,该程序给出了相同的消息:

\n\n
void moo(int) {};\nvoid moo(int*){};\n\nint main()\n{\n    &moo != nullptr;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

显然,其意图是引用 [over.over],它讨论了获取重载函数的地址。标准中的这个位置指定了可以在哪些上下文中使用重载函数名称(赋值的右侧、函数调用中的参数等)。然而它说

\n\n
\n

在未列出的上下文中,不得在没有参数的情况下使用重载函数名称。(强调我的)

\n
\n\n

现在,使用foo不带参数吗?g++ 似乎就这么沉下去了。然而它很高兴编译(&foo<char>)(1)

\n\n
(&moo)(1);\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
\n

重载函数名称前面可以加上 & 运算符 [...] [注意:重载函数名称周围的任何多余括号都将被忽略 (5.1)。\xe2\x80\x94尾注]

\n
\n