我有以下函数特征代码:
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&)>
{};
Run Code Online (Sandbox Code Playgroud)
和另一个具有以下签名的 Matrix 类:
#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;
};
Run Code Online (Sandbox Code Playgroud)
这是在我担任助教的课程中作为 C++ 练习的一部分完成的。我想尝试找出我的学生是否按照我们的预期将他们的参数作为常量引用传递。
例如
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);
}
Run Code Online (Sandbox Code Playgroud)
这对于未重载的运算符似乎很有效……但是,我无法对重载的运算符(例如 * 或构造函数)进行相同的测试。
这似乎是因为 decltype 无法区分重载方法,但是在过去的几个小时里我尝试了一些东西并且无法获得任何工作。
有什么建议?
谢谢
一组重载不是一种类型。decltype operator*当它过载时你不能,因为你必须先选择一个过载。这可以通过static_cast评论中已经建议的方式来完成。
这个答案解释了一个非常有用的习惯用法来检查类型的属性。为了完整起见,我在此处包含代码:
Run Code Online (Sandbox Code Playgroud)// 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,它不是你自己无法编写的。然而,它将大部分样板重构为上述通用部分,剩下的就是为所需的属性定义一个模板。对于具有所需属性的类型,该模板必须“正常”,对于没有所需属性的类型,它应该失败。
我决定使用 astatic_cast然后decltype就可以了。仅此一项看起来有点奇怪,但它所做的就是:当存在operator*所需的签名时成功,否则失败:
template <typename T>
using const_ref_derefop = decltype(static_cast< T&(T::*)(const T&) >(&T::operator*));
Run Code Online (Sandbox Code Playgroud)
您可以为其他运算符或其他签名编写相同的内容,用法是:
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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
10
Run Code Online (Sandbox Code Playgroud)
关键点实际上只是将&T::operator*转换为具有所需签名的成员函数指针。如果该签名没有过载,这将失败。