【问题标题】:How do I resolve this ambiguous template constructor call?如何解决这个模棱两可的模板构造函数调用?
【发布时间】:2016-03-22 14:43:18
【问题描述】:

我有一个矩阵类模板:

#include <iostream>
#include <array>
#include <initializer_list>
#include <utility>
#include <type_traits>
#include <cstddef>

enum ColumnFill {
    COLUMNS
};

template <typename T, std::size_t M, std::size_t N>
struct TMatrixMxN {
    TMatrixMxN(T x = T(0)) {
        std::cout << "Default" << std::endl;
    }

    TMatrixMxN(std::initializer_list<T> values) {
        std::cout << "Row initializer" << std::endl;
    }

    TMatrixMxN(std::initializer_list<T> values, ColumnFill dummy) {
        std::cout << "Column initializer" << std::endl;
    }

    TMatrixMxN(std::initializer_list<std::initializer_list<T>> values) {
        std::cout << "Value initializer" << std::endl;
    }

    TMatrixMxN(const std::array<std::array<T, N>, M> &values) {
        std::cout << "From array" << std::endl;
    }

    TMatrixMxN(const TMatrixMxN<T, M - 1, N - 1> &x) {
        std::cout << "From lower dimension" << std::endl;
    }

    TMatrixMxN(const TMatrixMxN &x) {
        std::cout << "Copy" << std::endl;
    }

    TMatrixMxN(TMatrixMxN &&x) {
        std::cout << "Move" << std::endl;
    }
};

typedef TMatrixMxN<float, 1, 1> Matrix1x1;
typedef TMatrixMxN<float, 2, 2> Matrix2x2;
typedef TMatrixMxN<float, 3, 3> Matrix3x3;
typedef TMatrixMxN<float, 3, 1> Matrix3x1;

在我使用任何维度都为 1 的矩阵之前,一切都很花哨:

int main() {
    std::array<std::array<float, 3>, 3> arr{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}};
    Matrix3x3 m1;
    Matrix3x3 m2({1, 2, 3});
    Matrix3x3 m3({1, 2, 3}, COLUMNS);
    Matrix3x3 m4({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
    Matrix3x3 m5(arr);
    Matrix3x3 m6(Matrix2x2({{1, 2}, {3, 4}}));
    Matrix3x3 m7(m6);
    Matrix3x3 m8(std::move(m7));

    std::cout << std::endl;

    TMatrixMxN<float, 3, 2>({{1, 2}, {3, 4}, {5, 6}});

    std::cout << std::endl;

    // PROBLEMS:
    Matrix3x1({{1}, {2}, {3}});  // error: ambiguous
    Matrix1x1({{1}});            // error: ambiguous
}

我不知道如何很好地解决这个问题(我希望那些调用调用 value-initializer 构造函数)

当使用g++ -std=c++11 Ambiguous.cpp 编译时,编译器认为以下构造函数是 3x1 矩阵的候选者:movecopyfrom-lower-dimension em>、值初始化器行初始化器。对于 1x1 矩阵,它还列出了 from-arraydefault

我尝试过的事情:

  • 使用 SFINAE 使 T 必须是算术类型 (std::is_arithmetic) 的重载条件,因为我认为它有时可能会认为它是 initializer_list,但它没有改变。我意识到这是一个无用的检查,因为它实际上已经很好地知道 T 在这个例子中是浮动的
  • 向这些构造函数添加explicit 关键字:value-initializerrow-initializercolumn-initializerfrom -arrayfrom-lower-dimension,因为我认为有一些隐式调用正在进行,但它也没有改变任何东西
  • 创建一个 typedef "fil"(作为 "float initializer list")并将代码转换为 {fil{1}},因为我意识到支撑的东西可能不会被解释为初始化列表 - 然后它按预期工作。但是我认为这个修复还不够好。

能做到吗?

【问题讨论】:

    标签: c++ templates call ambiguous overload-resolution


    【解决方案1】:

    一维情况下的歧义在这两个构造函数之间:

    TMatrixMxN(std::initializer_list<T> values);
    TMatrixMxN(std::initializer_list<std::initializer_list<T>> values)
    

    因为{{1}, {2}, {3}} 也可以解释为{1, 2, 3} 的过于激进的支撑版本。请注意,重要的不是NM 的大小,这仍然失败:

    Matrix3x3 m9({{1}, {2}, {3}}); // same error
    

    最简单的解决方案是通过将不太需要的构造函数转换为带有虚拟参数的构造函数模板来消除歧义:

    template <size_t _=M>
    TMatrixMxN(std::initializer_list<T> values) {
        std::cout << "Row initializer" << std::endl;
    }
    

    我们实际上并没有改变任何东西——我们只是把它做成了一个模板。现在,如果两个构造函数都匹配,initializer_list&lt;initializer_list&lt;T&gt;&gt; 将是首选,因为它不是模板。如果您想真正为M==1N==1 禁用此功能,您也可以在其中添加enable_if_t

    template <size_t m=M, size_t n=N, class = std::enable_if_t<(m != 1 && n != 1)>>
    TMatrixMxN(std::initializer_list<T> values) {
        std::cout << "Row initializer" << std::endl;
    }
    

    【讨论】:

    • 谢谢! :) 你能告诉我如何使用 enable_if_t 做到这一点吗?
    • 我明白了,我做了同样的事情,只是我没有添加新的模板参数(m=M,n=N),只是使用了 M 和 N,但这不起作用..可以你解释一下为什么我需要新的模板参数?
    • @ComradeBearabyte SFINAE 仅在替换的直接上下文中起作用......外部模板参数不在演绎的直接上下文中。我相信这里有更深入的解释。
    猜你喜欢
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 2019-08-19
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-19
    相关资源
    最近更新 更多