C++ SFINAE 构造函数也接受派生 class
C++ SFINAE constructor accepting derived class too
入门:
我有一个Vector
和一个Vector2D
class。前者应该包含一个允许逐元素类型转换的构造函数。派生的 class 也应该被允许。在某些情况下,它已经有效(请参见下面的示例),但我认为缺少一些 SFINAE 魔法。
Vector
#include <array>
#include <type_traits>
namespace mu {
template<std::size_t N, typename T>
class Vector {
public:
// ...
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N ||
(!std::is_base_of_v<Vector, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data({args...}) {}
// this should always be called for type casting:
template <std::size_t Nn, typename Tt>
Vector(const Vector<Nn, Tt> &other) {
static_assert(N == Nn, "Vector size mismatch");
for (std::size_t i = 0; i < N; i++) {
data[i] = static_cast<T>(other[i]);
}
}
Vector(const Vector &other) = default; // copy constructor
protected:
std::array<T, N> data;
};
}
Vector2D
namespace mu {
template<typename T>
class Vector2D : public Vector<2,T> {
public:
using Vector<2, T>::Vector; // inherit base class constructors
Vector2D(const Vector<2, T>& other) : Vector<2, T>(other) {}
// Vector2D specific functions, e.g. rotation
//...
};
}
示例(它们都应该编译)
// Example 1 (compiles)
mu::Vector<2, int> a{1, 2};
mu::Vector<2, float> b{a};
// Example 2 (compiles)
mu::Vector<2, int> c{1, 2};
mu::Vector2D<float> d{c};
// Example 3 (doesn't compile)
mu::Vector2D<int> e{1, 2};
mu::Vector<2, float> f{e};
// Example 4 (doesn't compile)
mu::Vector2D<int> g{1, 2};
mu::Vector2D<float> h{g};
错误(示例 3 和 4)
.../vector.h:63:27: error: no matching constructor for initialization of 'std::array<float, 2UL>'
Vector(TArgs... args) : data_({args...}) {}
代码试图调用错误的构造函数而不是包含类型转换的构造函数。我已经尝试 添加另一个约束 到 enable_if 项,但到目前为止没有好的结果。
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N ||
(!std::is_base_of_v<Vector, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data({args...}) {}
是错误的,你可能想要&&
Demo
但 std::is_convertible<TArgs, T>
或 std::is_constructible<T, TArgs>
似乎更合适。
没有“标签”的可变参数构造函数是危险的顺便说一句,因为它可以轻松捕获复制构造函数。
@Jarod42 为我指明了正确的方向
对于两个参数,我需要的是 &&
而不是 ||
。但是,std::is_base_of
并没有真正执行我想要的。我将其替换为 std::is_same
以检查 ...TArgs
提供的所有参数是否实际上与 Vector 已经包含的参数 T
.
属于同一类型
现在,只能通过给它提供确切的 N
个类型为 T
的参数来调用此构造函数
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N &&
(std::is_same_v<T, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data_({args...}) {}
入门:
我有一个Vector
和一个Vector2D
class。前者应该包含一个允许逐元素类型转换的构造函数。派生的 class 也应该被允许。在某些情况下,它已经有效(请参见下面的示例),但我认为缺少一些 SFINAE 魔法。
Vector
#include <array>
#include <type_traits>
namespace mu {
template<std::size_t N, typename T>
class Vector {
public:
// ...
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N ||
(!std::is_base_of_v<Vector, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data({args...}) {}
// this should always be called for type casting:
template <std::size_t Nn, typename Tt>
Vector(const Vector<Nn, Tt> &other) {
static_assert(N == Nn, "Vector size mismatch");
for (std::size_t i = 0; i < N; i++) {
data[i] = static_cast<T>(other[i]);
}
}
Vector(const Vector &other) = default; // copy constructor
protected:
std::array<T, N> data;
};
}
Vector2D
namespace mu {
template<typename T>
class Vector2D : public Vector<2,T> {
public:
using Vector<2, T>::Vector; // inherit base class constructors
Vector2D(const Vector<2, T>& other) : Vector<2, T>(other) {}
// Vector2D specific functions, e.g. rotation
//...
};
}
示例(它们都应该编译)
// Example 1 (compiles)
mu::Vector<2, int> a{1, 2};
mu::Vector<2, float> b{a};
// Example 2 (compiles)
mu::Vector<2, int> c{1, 2};
mu::Vector2D<float> d{c};
// Example 3 (doesn't compile)
mu::Vector2D<int> e{1, 2};
mu::Vector<2, float> f{e};
// Example 4 (doesn't compile)
mu::Vector2D<int> g{1, 2};
mu::Vector2D<float> h{g};
错误(示例 3 和 4)
.../vector.h:63:27: error: no matching constructor for initialization of 'std::array<float, 2UL>'
Vector(TArgs... args) : data_({args...}) {}
代码试图调用错误的构造函数而不是包含类型转换的构造函数。我已经尝试 添加另一个约束 到 enable_if 项,但到目前为止没有好的结果。
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N ||
(!std::is_base_of_v<Vector, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data({args...}) {}
是错误的,你可能想要&&
Demo
但 std::is_convertible<TArgs, T>
或 std::is_constructible<T, TArgs>
似乎更合适。
没有“标签”的可变参数构造函数是危险的顺便说一句,因为它可以轻松捕获复制构造函数。
@Jarod42 为我指明了正确的方向
对于两个参数,我需要的是 &&
而不是 ||
。但是,std::is_base_of
并没有真正执行我想要的。我将其替换为 std::is_same
以检查 ...TArgs
提供的所有参数是否实际上与 Vector 已经包含的参数 T
.
现在,只能通过给它提供确切的 N
个类型为 T
template <typename... TArgs,
std::enable_if_t<sizeof...(TArgs) == N &&
(std::is_same_v<T, TArgs> && ...), int> = 0>
Vector(TArgs... args) : data_({args...}) {}