【问题标题】:Error: redefinition of function template (or C2995)错误:重新定义函数模板(或 C2995)
【发布时间】:2018-05-05 19:51:01
【问题描述】:

GCC 和 MSVC 在使用模板进行矩阵乘法的方法上抛出编译错误,而 Clang 编译成功且没有任何错误,我不明白为什么。有人可以提出什么问题吗?

我认为实例化 operator*(matrix, matrix) 存在问题。

注意事项:(错误:重新定义函数模板,C2995:函数模板已经定义)【声明和定义在一个.hpp中,有header guards】

我在 gcc 7.2.0 及更高版本以及 MSVC 19.14.26428.1 及更高版本上对此进行了测试。我也使用 C++17 标准。

有问题的方法:

template <std::size_t Rows_lhs, std::size_t Columns_lhs,
          std::size_t Rows_rhs, std::size_t Columns_rhs>
friend constexpr matrix<value_type, Rows_lhs, Columns_rhs> operator*(
    const matrix<value_type, Rows_lhs, Columns_lhs>& lhs,
    const matrix<value_type, Rows_rhs, Columns_rhs>& rhs)
{
    static_assert(Columns_lhs == Rows_rhs, "Incorrect matrix for product!");

    matrix<value_type, Rows_lhs, Columns_rhs> result{};
    container<value_type, Rows_rhs> thatColumn{};

    for (size_type j = 0; j < Columns_rhs; ++j)
    {
        for (size_type k = 0; k < Rows_rhs; ++k)
        {
            thatColumn.at(k) = rhs(k, j);
        }

        for (size_type i = 0; i < Rows_lhs; ++i)
        {
            const auto thisRow = lhs(i);
            value_type summand{};
            for (size_type k = 0; k < Rows_rhs; ++k)
            {
                summand += thisRow.at(k) * thatColumn.at(k);
            }
            result(i, j) = summand;
        }
    }
    return result;
}

完整代码:

#include <iostream>
#include <string>
#include <cmath>
#include <iterator>
#include <algorithm>
#include <array>
#include <initializer_list>


namespace vv
{
template <class Type = double, std::size_t Rows = 1, std::size_t Columns = 1>
class matrix
{
public:
    using value_type                    = Type;
    using size_type                     = std::size_t;

    template <class Type = value_type, std::size_t N = Rows>
    using container                     = std::array<Type, N>;
    using row_container                 = container<value_type, Columns>;
    using row_container_reference       = container<value_type, Columns>&;    
    using const_row_container_reference = const container<value_type, Columns>&;

    using reference                     = value_type&;
    using const_reference               = const value_type&;

    using std_matrix                    = matrix<value_type, Rows, Columns>;


    static constexpr value_type EPS = static_cast<value_type>(1e-10);

    static_assert(std::is_arithmetic_v<value_type>, "Matrix elements type has to be arithmetic!");
    static_assert(Rows > 0 && Columns > 0, "Incorrect size parameters!");

    constexpr matrix() = default;

    constexpr matrix(const std::initializer_list<value_type> list)
    : _data()
    {
        size_type row_counter = 0;
        size_type col_counter = 0;
        for (const auto elem : list)
        {
            _data.at(row_counter).at(col_counter) = elem;
            ++col_counter;
            if (row_counter == Rows && col_counter == Columns)
            {
                break;
            }
            if (col_counter == Columns)
            {
                col_counter = 0;
                ++row_counter;
            }
        }
    }

    std::string get_dimension() const noexcept
    {
        return std::to_string(Rows) + std::string("x")
                + std::to_string(Columns);
    }

    constexpr const_reference operator()(const size_type i, const size_type j) const
    {
        return _data.at(i).at(j);
    }

    constexpr reference operator()(const size_type i, const size_type j)
    { 
        return _data.at(i).at(j);
    }

    constexpr const_row_container_reference& operator()(const size_type i) const
    {
        return _data.at(i);
    }

    constexpr row_container_reference& operator()(const size_type i)
    {
        return _data.at(i);
    }

    constexpr std_matrix& operator*=(const value_type num) noexcept
    {
        for (auto& row : _data)
        {
            for (auto& elem : row)
            {
                elem *= num;
            }
        }
        return *this;
    }

    friend constexpr std_matrix operator*(const std_matrix& mat, const value_type num) noexcept
    {
        std_matrix temp(mat);
        return (temp *= num);
    }

    friend constexpr std_matrix operator*(const value_type num, const std_matrix& mat) noexcept
    {
        return (mat * num);
    }

    friend std::ostream& operator<<(std::ostream& os, const std_matrix& mat)
    {
        os << "[" << mat.get_dimension() << "]\n";
        for (const auto& row : mat._data)
        {
            std::copy(std::begin(row), std::end(row),
                      std::ostream_iterator<value_type>(os, " "));
            os << '\n';
        }
        return os;
    }

    // PROBLEMS HERE BEGIN
    template <std::size_t Rows_lhs, std::size_t Columns_lhs,
              std::size_t Rows_rhs, std::size_t Columns_rhs>
    friend constexpr matrix<value_type, Rows_lhs, Columns_rhs> operator*(
        const matrix<value_type, Rows_lhs, Columns_lhs>& lhs,
        const matrix<value_type, Rows_rhs, Columns_rhs>& rhs)
    {
        static_assert(Columns_lhs == Rows_rhs, "Incorrect matrix for product!");

        matrix<value_type, Rows_lhs, Columns_rhs> result{};
        container<value_type, Rows_rhs> thatColumn{};

        for (size_type j = 0; j < Columns_rhs; ++j)
        {
            for (size_type k = 0; k < Rows_rhs; ++k)
            {
                thatColumn.at(k) = rhs(k, j);
            }

            for (size_type i = 0; i < Rows_lhs; ++i)
            {
                const auto thisRow = lhs(i);
                value_type summand{};
                for (size_type k = 0; k < Rows_rhs; ++k)
                {
                    summand += thisRow.at(k) * thatColumn.at(k);
                }
                result(i, j) = summand;
            }
        }
        return result;
    }
    // END

private:
    container<container<value_type, Columns>, Rows> _data;
};

} // namespace vv


int main()
{
    constexpr vv::matrix<double, 2, 1> a{ 1.0, 2.0 };
    constexpr vv::matrix<double, 1, 2> b{ 4.0, 3.0 };

    constexpr auto c = a * b; // This code occurs error.
    std::cout << c;
    return 0;
}

【问题讨论】:

  • 这里是邮政编码。
  • GCC 和 MSVC -- 您的帖子中缺少版本信息。
  • 并非如此,您应该丢弃与给定问题无关的代码。所以结果是一个 minimal 的例子再现了声明的错误。
  • @VasilyVasilyev -- 需要发布近 450 行代码才能重现错误 -- 如果错误与函数定义有关,您可以从删除开始函数体内的所有代码,因为它们与所讨论的错误无关。如果函数需要return,则返回一些虚拟值。
  • @JohnPerry,谢谢,我忘了提。固定。

标签: c++ templates gcc visual-c++ c++17


【解决方案1】:

问题在于operator*() 是在内部 模板matrix 类中定义的。

所以,当你定义一个matrix 对象时,比如matrix&lt;double, 1, 2&gt;,这个函数就被定义了;当您定义另一个具有相同类型和不同维度的对象时,例如matrix&lt;double, 2, 1&gt;,会重新定义完全相同的模板函数。

在我看来,没有什么要求该函数是 friend 的 matrix 所以 -- 建议 -- 在类内部删除它并在外部重写它,如下所示

template <class Type, std::size_t N>
using container = std::array<Type, N>;

using size_type                     = std::size_t;

template <typename value_type, std::size_t Rows_lhs, std::size_t Columns_lhs,
          std::size_t Rows_rhs, std::size_t Columns_rhs>
constexpr matrix<value_type, Rows_lhs, Columns_rhs> operator*(
    const matrix<value_type, Rows_lhs, Columns_lhs>& lhs,
    const matrix<value_type, Rows_rhs, Columns_rhs>& rhs)
{
    static_assert(Columns_lhs == Rows_rhs, "Incorrect matrix for product!");

    matrix<value_type, Rows_lhs, Columns_rhs> result{};
    container<value_type, Rows_rhs> thatColumn{};

    for (size_type j = 0; j < Columns_rhs; ++j)
    {
        for (size_type k = 0; k < Rows_rhs; ++k)
        {
            thatColumn.at(k) = rhs(k, j);
        }

        for (size_type i = 0; i < Rows_lhs; ++i)
        {
            const auto thisRow = lhs(i);
            value_type summand{};
            for (size_type k = 0; k < Rows_rhs; ++k)
            {
                summand += thisRow.at(k) * thatColumn.at(k);
            }
            result(i, j) = summand;
        }
    }
    return result;
}

如果你真的想要,你可以维护它friend,但只能在类中声明它matrix

template <typename value_type, std::size_t Rows_lhs, 
          std::size_t Columns_lhs, std::size_t Rows_rhs, 
          std::size_t Columns_rhs>
friend constexpr matrix<value_type, Rows_lhs, Columns_rhs> operator*(
    const matrix<value_type, Rows_lhs, Columns_lhs>& lhs,
    const matrix<value_type, Rows_rhs, Columns_rhs>& rhs); 

奖励(题外话)建议:无需定义 四 矩阵维度并用static_assert() 强加第二个(Columns_lhs)和第三个(Rows_rsh)是相等。

您可以将它们统一在一个模板参数中(midDim,在以下示例中)

template <typename value_type, std::size_t Rows_lhs, std::size_t midDim,
          std::size_t Columns_rhs>
constexpr matrix<value_type, Rows_lhs, Columns_rhs> operator*(
    const matrix<value_type, Rows_lhs, midDim>& lhs,
    const matrix<value_type, midDim, Columns_rhs>& rhs)
{
    matrix<value_type, Rows_lhs, Columns_rhs> result{};
    container<value_type, midDim> thatColumn{};

    for (size_type j = 0; j < Columns_rhs; ++j)
    {
        for (size_type k = 0; k < midDim; ++k)
        {
            thatColumn.at(k) = rhs(k, j);
        }

        for (size_type i = 0; i < Rows_lhs; ++i)
        {
            const auto thisRow = lhs(i);
            value_type summand{};
            for (size_type k = 0; k < midDim; ++k)
            {
                summand += thisRow.at(k) * thatColumn.at(k);
            }
            result(i, j) = summand;
        }
    }
    return result;
}

【讨论】:

  • 嗯? std_matrix(operator* 的参数的一部分)是matrix&lt;value_type, Rows, Columns&gt;,所以你不会为每个模板实例化得到不同的operator* 吗? (编辑:哦等等,有多个operator* 重载。你的答案是对的。)
  • @max66,非常感谢!但是 Clang 用什么魔法让这个错误消失呢?
  • @hvd - ehmmm.. 我没有看到 std_matrix 的参数 friend operator*() 在 matrix 中定义;如果我没记错的话,operator*() 的签名取决于value_type 而不是来自Rows 和Columns;所以(像往常一样:如果我没记错的话)用不同的value_type 定义不同matrix 类型的不同对象是安全的,但是如果你定义两个matrix 具有相同value_type 和不同尺寸的对象,您重新定义了相同的运算符。
  • @VasilyVasilyev - clang 中没有错误也让我感到惊讶。
  • @VasilyVasilyev - 我已经简化了问题并询问了another question,希望有人能说出我们在clang++和g++之间的正确选择
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多