【问题标题】:Arguments to a template function aren't doing any implicit conversion模板函数的参数没有进行任何隐式转换
【发布时间】:2011-06-21 22:19:09
【问题描述】:

由于某种奇怪的原因,我无法将这段代码中的模板参数隐式转换为兼容类型。

#include <type_traits>

template <typename T, unsigned D>
struct vec;

template <>
struct vec<float, 2> {
    typedef float scalar;
    static constexpr unsigned dimension = 2;

    float x, y;
    float&       operator[] (unsigned i)       { return (&x)[i]; }
    float const& operator[] (unsigned i) const { return (&x)[i]; }
};


template <typename L, typename R>
struct add;

template <typename L, typename R, unsigned D>
struct add<vec<L, D>, vec<R, D>> {
    typedef vec<L, D> left_type;
    typedef vec<R, D> right_type;
    typedef vec<typename std::common_type<L, R>::type, D> return_type;

    add(left_type l, right_type r)
        : left(l),
          right(r)
    {}

    operator return_type() const
    {
        return_type result;
        for (unsigned i = 0; i < D; ++i)
            result[i] = left[i] + right[i];
        return result;
    }

    left_type  left;
    right_type right;
};


template <typename L, typename R, unsigned D>
add<vec<L, D>, vec<R, D>>
operator+(vec<L, D> const& lhs, vec<R, D> const& rhs)
{
    return {lhs, rhs};
}


int main()
{
    vec<float, 2> a, b, c;
    vec<float, 2> result = a + b + c;
}

失败:

prog.cpp: In function 'int main()':
prog.cpp:55:36: error: no match for 'operator+' in 'operator+ [with L = float, R = float, unsigned int D = 2u](((const vec<float, 2u>&)((const vec<float, 2u>*)(& a))), ((const vec<float, 2u>&)((const vec<float, 2u>*)(& b)))) + c'

所以如果我是正确的,编译器应该看到 main 函数中的代码是这样的:

  • ((a + b) + c)
  • 计算a + b
  • 使用add&lt;...&gt; 中的转换运算符将a + b 的结果从add&lt;...&gt; 转换为vec&lt;float, 2&gt;
  • 计算(a + b) + c

但它从不进行隐式转换。如果我将 (a + b) 的结果显式转换为 vec,则代码可以正常工作。

【问题讨论】:

  • 这是一大堆代码。你能简化你的例子吗?
  • @Oli:实际上这是一个非常简单明了的表达式模板实现。这几乎不包含算法代码,它主要是使这项工作的机制。我真的不明白这里有什么可批评的。
  • @sbi:问题是我想帮忙,但我累了,真的不想阅读所有代码!哦,好吧...
  • @sbi 我实际上一直在试图弄清楚这到底是什么。我知道我以前见过它,但我不记得它的名字了。谢谢!
  • 您正在编写所谓的“表达式模板”。尝试搜索该术语。例如here 是 Angelika Langer 的一篇不错的文章。这被认为是一种先进的技术。所以祝你学习愉快! :-)

标签: c++ templates c++11 template-meta-programming expression-templates


【解决方案1】:

我将回避您的实际问题,而是提出建议:与其从头开始编写所有这些复杂的样板,不如查看 Boost.Proto,它已经处理了所有棘手的细节:

Proto 是一个用于在 C++ 中构建领域特定嵌入式语言的框架。它提供了用于构造、类型检查、转换和执行表达式模板的工具。更具体地说,Proto 提供:

  • 表达式树数据结构。
  • 一种为表达式提供额外行为和成员的机制。
  • 从表达式构建树的运算符重载。
  • 用于定义表达式必须符合的语法的实用程序。
  • 一种用于立即执行表达式模板的可扩展机制。
  • 一组可扩展的树转换,适用于表达式树。

另请参阅库作者的 Expressive C++ 系列文章,这或多或少可以作为(优秀)深入的 Boost.Proto 教程。

【讨论】:

  • 我会看一看,但它看起来很吓人。我想做的就是提供某些操作的专业化,以便我可以优化它们。例如,可以使用 SIMD 优化 (a * b) + (c * d)。所以,我会覆盖 add, mul> 来调用 vDSP_vmma(我使用 vDSP 进行 SIMD 优化)。
  • @franticfantom :与从头开始编写表达式模板相比令人生畏? ;-] 如果您按照本教程进行操作,我认为您会发现基于 Proto 的实现的代码行数比手写表达式模板少一个数量级,而且您会遇到更少的代码像促使这个问题开始的那个特质。 :-]
  • @franticfantom :另外,您应该对Joel Falcou 的当前状态提出错误NT2,据我所知,它正在围绕Boost.Proto 进行重写,因此可能完美解决你的问题。
  • @ildjarn:我也经常更喜欢来自我自己的混乱的模板错误消息,而不是一些提升代码会吐给我的那些。
  • @sbi :鉴于 Eric Niebler 是 leading the crusade to improve library error messages,我保证 Boost.Proto 产生的错误比其他任何人的表达式模板代码更容易理解。 “错误的模板错误是库错误,应该这样报告。”确实,易于调试(通过良好的注释,放置良好的静态断言)被吹捧为首先使用 Boost.Proto 的主要原因。对您自己的代码没有任何冒犯之意。 ;-]
【解决方案2】:

在模板参数推导过程中不使用大多数转换。

当您调用 operator+ 重载时,您依赖于模板参数推导:它仅在两个参数的类型为 vec&lt;...&gt; 时才可调用,但当您尝试调用它时,左侧参数的类型为 add&lt;...&gt; .编译器无法判断出您的真正意思是要调用该重载(并且不允许猜测),因此会出现错误。

【讨论】:

  • 又名,写两次operator+。 ://
  • 四次:(1)vec + vec,(2)vec + add,(3)add + vec,(4)add + add。所有四种组合都是可能的:v + v(v + v) + vv + (v + v)(v + v) + (v + v)。或者(并且最好),如果您的类在命名空间中,您应该能够放宽对模板的限制(即,让您的 operator+ 采用任意的 TU 而不是 vec&lt;...&gt;vec&lt;...&gt;) 并让 ADL 做正确的事。
  • @James 第一种方法在我加减法、乘法、除法等时变得不可能。后一种方法我遇到了一些麻烦,但我还有一些方法可以尝试。如果我让 operator+ 带 T 和 U,那意味着我需要专门化 add 来简化左右两边的表达式,对吗?
  • 我得考虑一下。为了快速回答,其他人会比我更了解(其他人,可能是:()。我并没有真正使用过表达式模板。不过我会考虑一下。(如果你想得到,你可能不想接受这个更好地回答“我应该怎么做才能使这项工作?”问题;很多人避免用公认的答案回答问题。)
  • @Alf:嗯,我不是这方面的专家,重要的是 OP 得到了最好的答案。我有足够的“积分”:-)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
相关资源
最近更新 更多