编译器错误模板参数列表<对照

时间:2015-12-10 09:26:14

标签: c++ templates

我有一个样式的模板函数:

template <int Exponent> DERIVED_TYPE pow(TYPE const x);

此函数在模板结构中作为友元函数内联定义:

template <ARGUMENTS>
struct unit {
    typedef unit<ARGUMENTS> type;

    ....
    template <int Exponent>
    friend constexpr unit_pow_t<type, Exponent> pow(type const x) { ... }
};

这是因为将一个单位的值与一个幂相加必须随着该值改变单位。

当我尝试使用它省略Exponent时,我可以看到编译器考虑匹配的候选者:

src/model/Tool.cpp:113:3: error: no matching function for call to 'pow'
                pow(1._m);
                ^~~
src/model/../units/Units.hpp:2266:46: note: candidate template ignored: couldn't infer template argument 'Exponent'
        friend constexpr unit_pow_t<type, Exponent> pow(type const x) {
                                                    ^
/usr/include/math.h:255:8: note: candidate function not viable: requires 2 arguments, but 1 was provided
double  pow(double, double);
        ^

到目前为止事情正如预期的那样,可以看到模板,但当然需要指定Exponent。然而,当我这样做时会发生意想不到的事情:

src/model/Tool.cpp:113:6: error: comparison between pointer and integer ('double (*)(double, double)' and 'int')
                pow<3>(1._m);
                ~~~^~

编译器将 pow 视为“ double pow(double,double)”的地址,并将&lt; 3 解释为将函数指针与整数进行比较。 clang 3.4,3.6和GCC 5.2会出现问题。

我的问题是,如何让编译器相信&lt; 3&gt;是模板参数列表吗?

更新

我终于设法创建了一个最小的例子,对不完整的问题感到抱歉:

template <int Exp>
struct metre {
    double value;
    template <int Exponent>
    friend constexpr metre<Exp * Exponent> pow(metre<Exp> const x) {
        return {0};
    }
};

int main() {
    pow<2>(metre<1>{1});
    return 0;
};

似乎没有看到战俘:

targs.cpp:11:2: error: use of undeclared identifier 'pow'
        pow<2>(metre<1>{1});
        ^

如果我包含cmath,我有与以前相同的诊断:

targs.cpp:13:5: error: comparison between pointer and integer ('double (*)(double, double)' and 'int')
        pow<2>(metre<1>{1});
        ~~~^~
1 error generated.

因此,“ double pow(double,double)”的存在只会掩盖模板未被看到的问题。那么问题是,为什么编译器看不到pow&lt;&gt;()?

3 个答案:

答案 0 :(得分:3)

friend template没有必要(也没有逻辑)。使用免费功能:

template <int Exp>
struct metre {
    double value;
};

template<int B, int A>
constexpr auto pow(metre<A> const x) -> metre<A*B>
{
        return metre<A*B>{x.value};
}

int main() {
    metre<2> result = pow<2>(metre<1>{1});
    return 0;
};

Live demo on coliru

答案 1 :(得分:3)

这是模板朋友问题,有两个怪癖:函数名称为pow;模板朋友有自己的模板参数!

根据经验,只要在类模板中使用friend,就要保持警惕。

为了更容易解决问题:在MCVE中发布,metre的类定义不会导致名称pow被声明(这将是稍后解释)。您的错误消息即将发生,因为实际上有一个名称pow的可见声明:它位于C标准库标题math.h中。 &#34;指针&#34;在错误消息中是一个指向此函数的函数指针。

不要将功能命名为与C标准库中的功能相同,这是一个好主意。无论如何,这些都可能被定义为预处理器宏,从而导致进一步的麻烦。

对于这篇文章的其余部分,我将假设MCVE已pow换出pok,以避免这种皱纹。然后生成一个合理的错误消息:

po.cc:13:5: error: 'pok' was not declared in this scope
    pok<2>(metre<1>{1});
    ^

现在转到主要问题。

讨论问题的基本版本here。问题是在类模板中声明友元函数 NOT 使得友元函数也是一个模板函数,其参数与类模板相同。

实际上,发生的情况是,对于类模板的每个实例化,friend声明声明该实例化的非模板朋友。

要查看此操作,请在您的MCVE中添加第metre<1> m;行作为main的第一行。这会实例化metre<1>,导致template<int Exponent> pok(metre<1>)存在,因此编译器会识别下一行的pok

实际上,任何特定的实例化都有效,而不仅仅是metre<1>,因为这至少允许pok的名称查找成功,然后在发生重载解析时(两阶段查找!) ,参数metre<1>导致metre<1>被实例化。

结束说明:我不完全确定上述说明是正确的 - 模板朋友非常复杂。也许事实证明,实际上pok应该被声明并且编译器被窃听。但我同意rubenvb的建议,即最好不要使用friend来完全避免这种情况。

答案 2 :(得分:0)

在前面的答案的帮助下,我想出了一个解决方法。

问题在于朋友是定义的(或多或少是预期的),但在类/结构范围之外是未知的。所以编译器找不到它们。

解决方案是在结构之外放置一个声明:

$errors['status'] = 'error';
return new JsonResponse($errors, 200);

请注意,没有必要提供匹配声明。具有正确名称的函数的任何模板声明似乎都允许编译器发现朋友模板函数。