重载成员函数的函数特征
function traits for overloaded member functions
我有以下函数特征代码:
template<typename T>
struct function_traits;
template<class F>
struct function_traits;
// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
template<class R, class... Args>
struct function_traits<R(Args...)>
{
using return_type = R;
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};
// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};
和另一个具有以下签名的矩阵 class:
#include <iostream>
/**
* @struct MatrixDims
* @brief Matrix dimensions container
*/
typedef struct MatrixDims
{
int rows, cols;
} MatrixDims;
/**
* Matrix class (only for 2D matrices)
*/
class Matrix
{
public:
const std::string errInvalidDims = "Error: Invalid matrices dimensions for this operation.";
const std::string errIndexOutOfRange = "Error: Index out of range.";
const std::string errFailToLoadMatrix = "Error: Failed to load elements to matrix from file.";
/**
* Matrix object constructor
* @param n Amount of rows
* @param m Amount of columns
*/
Matrix(int n, int m);
/***
* Copy Constructor for Matrix
* @param m Matrix to be copied
*/
Matrix(const Matrix &m);
/**
* default C'tor
*/
Matrix(): Matrix(1,1) {};
/***
* Destructor for Matrix
*/
~Matrix();
//########################## Operator Overloading Functions ########################################
/**
* Operator overload for = operator
* @param m Matrix to be copied
* @return reference to calling object
*/
Matrix& operator=(const Matrix &M);
/**
* Operator overload for Matrix multiplication.
* @param M: right side Matrix
* @return new matrix with new dimensions
* (Num of rows = number of rows of calling matrix, Num of Columns = num of columns as right side matrix)
*/
Matrix operator*(const Matrix &M) const;
/**
* Operator Overload for Matrix addition, on invalid
* @param M Right side Matrix
* @return new matrix with same dimension as calling matrix
*/
Matrix operator+(const Matrix &M) const;
/**
* Operator Overload for Matrix +=
* @param M Right side Matrix
* @return reference to the calling matrix after the addition
*/
Matrix& operator+=(const Matrix &M);
/**
* Overload to () operator, allows access and change of the i,j coordinate
* @param i - ith row
* @param j - jth column
* @return reference to the value stored in the i,j coordinate
*/
float& operator()(int i, int j);
/**
* Overload to () operator (const version), allows access to i,j coordinate
* @param i - ith row
* @param j - jth column
* @return reference to the value stored in the i,j coordinate
*/
const float& operator()(int i, int j) const;
/**
* Overload to the [] operator, allows access and modification of the ith Matrix value as if
* it were flattened in to a vector.
* For example: Matrix M of shape (2,3) M[5] = M(1,2) (with column and row numbers starting from 0)
* @param i-ith coordinate of the flattened vector
* @return reference to the value in the ith coordinate
*/
float& operator[](int i);
/**
* Overload to the [] operator,(const version) allows access of the ith Matrix value as if
* it were flattened in to a vector.
* For example: Matrix M of shape (2,3) M[5] = M(1,2) (with column and row numbers starting from 0)
* @param i-ith coordinate of the flattened vector
* @return reference to the value in the ith coordinate
*/
const float& operator[](int i) const;
/**
* Image Prints the calling Matrix object
* @param os
* @return reference to the os
*/
friend std::ostream& operator<<(std::ostream &os, const Matrix& M);
/**
* Input of binary data to matrix
* @param is input stream to read from
* @param M matrix to fill
* @return ref to is
*/
friend std::istream& operator>>(std::istream &is, Matrix& M);
/**
* Scalar multiplication on the right of the matrix
* @param left
* @param right
* @return
*/
Matrix operator*(const float& right);
/**
* Scalar multiplication on the left of the matrix
* @param left
* @param right
* @return
*/
friend Matrix operator*(const float& left, const Matrix& right);
/***
* getter for the number of rows
*/
int getRows() const {return rows;}
/***
* getter for the number of columns
*/
int getCols() const {return cols;}
/**
* Transforms a matrix to a column vector.
* @return ref to this
*/
Matrix& vectorize() {
rows = rows * cols;
cols = 1;
return *this;
}
/**
* Plain prints this matrix, simply prints the elemnts space separated.
*/
void plainPrint(){
for(int i = 0; i < getRows(); i++){
for(int j = 0; j < getCols(); j++){
std::cout << (*this)(i,j) << " ";
}
std::cout << std::endl;
}
}
/**
* Plain prints this matrix, simply prints the elemnts space separated. const version
*/
void plainPrint() const{
for(int i = 0; i < getRows(); i++){
for(int j = 0; j < getCols(); j++){
std::cout << (*this)(i,j) << " ";
}
std::cout << std::endl;
}
}
private:
float *matrix;
int rows, cols;
};
这是在我担任助教的课程中作为 C++ 练习的一部分完成的。
我想尝试看看我的学生是否按照我们的预期将他们的参数作为 const 引用传递。
例如
using Traits = function_traits<decltype(&Matrix::operator=)>;
if(!std::is_same<const Matrix&, Traits::argument<1>::type>::value)
{
std::cerr << "Operator= does not accept by const reference" << std::endl;
exit(2);
}
这似乎适用于未重载的运算符...但是我无法对重载的运算符(例如 * 或构造函数)进行相同的测试。
这似乎是因为 decltype 无法区分重载方法,但是在过去的几个小时里我尝试了一些方法,但没有任何效果。
有什么建议吗?
编辑*
一个不起作用的例子:
谢谢
一组重载不是一种类型。重载时不能 decltype
operator*
,因为你必须先选择一个重载。这可以通过 static_cast
完成,正如评论中已经建议的那样。
这个 answer 解释了一个非常有用的习惯用法来检查类型的属性。为了完整起见,我在此处包含代码:
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
它依赖于 SFINAE,它不是你不能自己写的东西。但是,它将大部分样板重构为上述通用部分,剩下的就是为所需 属性 定义模板。对于具有所需 属性 的类型,该模板必须“正常”,对于不具有所需 属性.
的类型,它应该失败
我决定使用 static_cast
,然后再使用 decltype
。这看起来有点奇怪,但它所做的只是:当存在所需签名的 operator*
时成功,否则失败:
template <typename T>
using const_ref_derefop = decltype(static_cast< T&(T::*)(const T&) >(&T::operator*));
你可以为其他运营商或其他签名编写相同的用法是:
struct A {
A& operator*(const A&);
A& operator*(A);
};
struct B {
B& operator*(B);
};
int main() {
std::cout << detect<A,const_ref_derefop>::value;
std::cout << detect<B,const_ref_derefop>::value;
}
输出:
10
关键点实际上只是将 &T::operator*
转换为具有所需签名的成员函数指针。如果该签名没有重载,这将失败。
我有以下函数特征代码:
template<typename T>
struct function_traits;
template<class F>
struct function_traits;
// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
template<class R, class... Args>
struct function_traits<R(Args...)>
{
using return_type = R;
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};
// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};
和另一个具有以下签名的矩阵 class:
#include <iostream>
/**
* @struct MatrixDims
* @brief Matrix dimensions container
*/
typedef struct MatrixDims
{
int rows, cols;
} MatrixDims;
/**
* Matrix class (only for 2D matrices)
*/
class Matrix
{
public:
const std::string errInvalidDims = "Error: Invalid matrices dimensions for this operation.";
const std::string errIndexOutOfRange = "Error: Index out of range.";
const std::string errFailToLoadMatrix = "Error: Failed to load elements to matrix from file.";
/**
* Matrix object constructor
* @param n Amount of rows
* @param m Amount of columns
*/
Matrix(int n, int m);
/***
* Copy Constructor for Matrix
* @param m Matrix to be copied
*/
Matrix(const Matrix &m);
/**
* default C'tor
*/
Matrix(): Matrix(1,1) {};
/***
* Destructor for Matrix
*/
~Matrix();
//########################## Operator Overloading Functions ########################################
/**
* Operator overload for = operator
* @param m Matrix to be copied
* @return reference to calling object
*/
Matrix& operator=(const Matrix &M);
/**
* Operator overload for Matrix multiplication.
* @param M: right side Matrix
* @return new matrix with new dimensions
* (Num of rows = number of rows of calling matrix, Num of Columns = num of columns as right side matrix)
*/
Matrix operator*(const Matrix &M) const;
/**
* Operator Overload for Matrix addition, on invalid
* @param M Right side Matrix
* @return new matrix with same dimension as calling matrix
*/
Matrix operator+(const Matrix &M) const;
/**
* Operator Overload for Matrix +=
* @param M Right side Matrix
* @return reference to the calling matrix after the addition
*/
Matrix& operator+=(const Matrix &M);
/**
* Overload to () operator, allows access and change of the i,j coordinate
* @param i - ith row
* @param j - jth column
* @return reference to the value stored in the i,j coordinate
*/
float& operator()(int i, int j);
/**
* Overload to () operator (const version), allows access to i,j coordinate
* @param i - ith row
* @param j - jth column
* @return reference to the value stored in the i,j coordinate
*/
const float& operator()(int i, int j) const;
/**
* Overload to the [] operator, allows access and modification of the ith Matrix value as if
* it were flattened in to a vector.
* For example: Matrix M of shape (2,3) M[5] = M(1,2) (with column and row numbers starting from 0)
* @param i-ith coordinate of the flattened vector
* @return reference to the value in the ith coordinate
*/
float& operator[](int i);
/**
* Overload to the [] operator,(const version) allows access of the ith Matrix value as if
* it were flattened in to a vector.
* For example: Matrix M of shape (2,3) M[5] = M(1,2) (with column and row numbers starting from 0)
* @param i-ith coordinate of the flattened vector
* @return reference to the value in the ith coordinate
*/
const float& operator[](int i) const;
/**
* Image Prints the calling Matrix object
* @param os
* @return reference to the os
*/
friend std::ostream& operator<<(std::ostream &os, const Matrix& M);
/**
* Input of binary data to matrix
* @param is input stream to read from
* @param M matrix to fill
* @return ref to is
*/
friend std::istream& operator>>(std::istream &is, Matrix& M);
/**
* Scalar multiplication on the right of the matrix
* @param left
* @param right
* @return
*/
Matrix operator*(const float& right);
/**
* Scalar multiplication on the left of the matrix
* @param left
* @param right
* @return
*/
friend Matrix operator*(const float& left, const Matrix& right);
/***
* getter for the number of rows
*/
int getRows() const {return rows;}
/***
* getter for the number of columns
*/
int getCols() const {return cols;}
/**
* Transforms a matrix to a column vector.
* @return ref to this
*/
Matrix& vectorize() {
rows = rows * cols;
cols = 1;
return *this;
}
/**
* Plain prints this matrix, simply prints the elemnts space separated.
*/
void plainPrint(){
for(int i = 0; i < getRows(); i++){
for(int j = 0; j < getCols(); j++){
std::cout << (*this)(i,j) << " ";
}
std::cout << std::endl;
}
}
/**
* Plain prints this matrix, simply prints the elemnts space separated. const version
*/
void plainPrint() const{
for(int i = 0; i < getRows(); i++){
for(int j = 0; j < getCols(); j++){
std::cout << (*this)(i,j) << " ";
}
std::cout << std::endl;
}
}
private:
float *matrix;
int rows, cols;
};
这是在我担任助教的课程中作为 C++ 练习的一部分完成的。 我想尝试看看我的学生是否按照我们的预期将他们的参数作为 const 引用传递。
例如
using Traits = function_traits<decltype(&Matrix::operator=)>;
if(!std::is_same<const Matrix&, Traits::argument<1>::type>::value)
{
std::cerr << "Operator= does not accept by const reference" << std::endl;
exit(2);
}
这似乎适用于未重载的运算符...但是我无法对重载的运算符(例如 * 或构造函数)进行相同的测试。
这似乎是因为 decltype 无法区分重载方法,但是在过去的几个小时里我尝试了一些方法,但没有任何效果。
有什么建议吗?
编辑*
一个不起作用的例子:
谢谢
一组重载不是一种类型。重载时不能 decltype
operator*
,因为你必须先选择一个重载。这可以通过 static_cast
完成,正如评论中已经建议的那样。
这个 answer 解释了一个非常有用的习惯用法来检查类型的属性。为了完整起见,我在此处包含代码:
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf. template <typename...> using void_t = void; // Primary template handles all types not supporting the operation. template <typename, template <typename> class, typename = void_t<>> struct detect : std::false_type {}; // Specialization recognizes/validates only types supporting the archetype. template <typename T, template <typename> class Op> struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
它依赖于 SFINAE,它不是你不能自己写的东西。但是,它将大部分样板重构为上述通用部分,剩下的就是为所需 属性 定义模板。对于具有所需 属性 的类型,该模板必须“正常”,对于不具有所需 属性.
的类型,它应该失败我决定使用 static_cast
,然后再使用 decltype
。这看起来有点奇怪,但它所做的只是:当存在所需签名的 operator*
时成功,否则失败:
template <typename T>
using const_ref_derefop = decltype(static_cast< T&(T::*)(const T&) >(&T::operator*));
你可以为其他运营商或其他签名编写相同的用法是:
struct A {
A& operator*(const A&);
A& operator*(A);
};
struct B {
B& operator*(B);
};
int main() {
std::cout << detect<A,const_ref_derefop>::value;
std::cout << detect<B,const_ref_derefop>::value;
}
输出:
10
关键点实际上只是将 &T::operator*
转换为具有所需签名的成员函数指针。如果该签名没有重载,这将失败。