可变模板错误 - MSVS2013编译,clang-3.5没有

时间:2015-08-29 23:13:20

标签: c++ c++11 c++14 variadic-templates clang++

下面的代码使用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?

2 个答案:

答案 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}}的原始调用不会编译,因为它尝试通过自身递归声明其返回类型(通过两个参数版本,仍然引用相同的模板)。这是不允许的。是的,我们都知道递归是明确定义的,并最终触底,但语言不允许这样做。 (仍在寻找正式的报价......)