下面的代码使用MSVC 2013进行编译和运行,但不是clang ++ 3.6。 哪个编译器正确?
MSVC 2013编译并执行代码,打印 26.04 :
#include <iostream>
template <typename T, typename ... U>
auto mul(T t, U ... u) -> decltype(t * mul(u ...))
{
return t * mul(u ...);
}
template <typename T>
T mul(T t) { return t; }
int main()
{
std::cout << mul(2., 3.1, 4.2) << std::endl;
}
然而,使用clang ++ - 3.6进行编译会产生错误:
$ clang++ test.cpp -stdlib=libc++ -Wall -Wextra -std=c++14
prog.cc:14:15: error: no matching function for call to 'mul'
std::cout << mul(2., 3.1, 4.2) << std::endl;
^~~
prog.cc:4:6: note: candidate template ignored: substitution failure [with T = double, U = <double, double>]: use of undeclared identifier 'mul'
auto mul(T t, U ... u) -> decltype(t * mul(u ...))
^ ~~~
prog.cc:10:3: note: candidate function template not viable: requires single argument 't', but 3 arguments were provided
T mul(T t) { return t; }
^
1 error generated.
mul的声明是否无法确定返回typedecl?
答案 0 :(得分:1)
您正尝试根据mul
的返回类型定义mul
的返回类型。更确切地说,在mul
表达式中使用decltype
之前,它是在完全声明之前(这正是编译器错误告诉你的)。
MSVC的模板实例化以非标准符合的方式工作,这就是为什么我认为clang是正确的 - 然而,它可以是实现定义但我不这么认为(也许有人知道标准的相关部分)。
答案 1 :(得分:1)
首先,如果将mul(2., 3.1)
中的调用替换为mul
(两个参数),则代码仍然无法在Clang和GCC中编译。但在这种情况下,它不会简单地编译,因为在多参数版本之后,单个参数mul(2., 3.1)
被声明为。单参数版本在声明多参数版本时尚不清楚。如果将单参数声明移到顶部,decltype(t * mul(u ...))
调用将编译。它编译是因为返回类型规范mul
引用了已经完全声明的单参数版本mul(2., 3.1, 4.2)
。
其次,具有三个参数{{1}}的原始调用不会编译,因为它尝试通过自身递归声明其返回类型(通过两个参数版本,仍然引用相同的模板)。这是不允许的。是的,我们都知道递归是明确定义的,并最终触底,但语言不允许这样做。 (仍在寻找正式的报价......)