如何解决这个不明确的模板构造函数调用?
How do I resolve this ambiguous template constructor call?
我有一个矩阵class模板:
#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 矩阵的候选对象:move、copy、 来自较低维度、值初始化器、行初始化器。对于 1x1 矩阵,它还列出 from-array 和 default.
我尝试过的事情:
- 使用 SFINAE 做一个重载条件,T 必须是算术类型 (
std::is_arithmetic
),因为我认为它有时可能会认为它是一个 initializer_list
,但它没有改变任何东西。我意识到这是一个无用的检查,因为它实际上已经很好地知道 T 在这个例子中是浮动的
- 向这些构造函数添加
explicit
关键字:value-initializer、row-initializer、 column-initializer、from-array 和 from-lower-dimension,因为我认为正在进行一些隐式调用, 但它也没有改变任何东西
- 制作 typedef "fil"(如 "float initializer list")并将代码转换为
{fil{1}}
,因为我意识到支撑的东西可能不会被解释为初始化列表 - 然后它起作用了如预期。但是我认为这个修复还不够好。
完全可以做到吗?
一维情况下的歧义在这两个构造函数之间:
TMatrixMxN(std::initializer_list<T> values);
TMatrixMxN(std::initializer_list<std::initializer_list<T>> values)
因为 {{1}, {2}, {3}}
也可以解释为 {1, 2, 3}
的过于激进的支撑版本。请注意,重要的不是大小 N
或 M
,这仍然失败:
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<initializer_list<T>>
将是首选,因为它不是模板。如果你真的想为 M==1
或 N==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;
}
我有一个矩阵class模板:
#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 矩阵的候选对象:move、copy、 来自较低维度、值初始化器、行初始化器。对于 1x1 矩阵,它还列出 from-array 和 default.
我尝试过的事情:
- 使用 SFINAE 做一个重载条件,T 必须是算术类型 (
std::is_arithmetic
),因为我认为它有时可能会认为它是一个initializer_list
,但它没有改变任何东西。我意识到这是一个无用的检查,因为它实际上已经很好地知道 T 在这个例子中是浮动的 - 向这些构造函数添加
explicit
关键字:value-initializer、row-initializer、 column-initializer、from-array 和 from-lower-dimension,因为我认为正在进行一些隐式调用, 但它也没有改变任何东西 - 制作 typedef "fil"(如 "float initializer list")并将代码转换为
{fil{1}}
,因为我意识到支撑的东西可能不会被解释为初始化列表 - 然后它起作用了如预期。但是我认为这个修复还不够好。
完全可以做到吗?
一维情况下的歧义在这两个构造函数之间:
TMatrixMxN(std::initializer_list<T> values);
TMatrixMxN(std::initializer_list<std::initializer_list<T>> values)
因为 {{1}, {2}, {3}}
也可以解释为 {1, 2, 3}
的过于激进的支撑版本。请注意,重要的不是大小 N
或 M
,这仍然失败:
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<initializer_list<T>>
将是首选,因为它不是模板。如果你真的想为 M==1
或 N==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;
}