LostOfThought
LostOfThought

Reputation: 123

std::enable_if With Non-Type Template Parameters

how would you go about using non-type template parameter comparison in a std::enable_if? I can not figure out how to do this again. (I once had this working, but I lost the code so I can't look back on it, and I can't find the post I found the answer in either.)

template<int Width, int Height, typename T>
class Matrix{
    static
    typename std::enable_if<Width == Height, Matrix<Width, Height, T>>::type
    Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
            elements[y][y] = T(1);
        }
        return ret;
    }
}

Edit: Fixed missing bracket as pointed out in comments.

Upvotes: 8

Views: 4137

Answers (2)

alfC
alfC

Reputation: 16242

It all depends on what kind of error/failure you want to raise on invalid code. Here it is one possibility (leaving aside the obvious static_assert(Width==Height, "not square matrix");)

(C++98 style)

#include<type_traits>
template<int Width, int Height, typename T>
class Matrix{
public:
    template<int WDummy = Width, int HDummy = Height>
    static typename std::enable_if<WDummy == HDummy, Matrix>::type
    Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
        // elements[y][y] = T(1);
        }
        return ret;
    }
};

int main(){
    Matrix<5,5,double> m55;
    Matrix<4,5,double> m45; // ok
    Matrix<5,5, double> id55 = Matrix<5,5, double>::Identity(); // ok
//  Matrix<4,5, double> id45 = Matrix<4,5, double>::Identity(); // compilation error! 
//     and nice error: "no matching function for call to ‘Matrix<4, 5, double>::Identity()"
}

EDIT: In C++11 the code can be more compact and clear, (it works in clang 3.2 but not in gcc 4.7.1, so I am not sure how standard it is):

(C++11 style)

template<int Width, int Height, typename T>
class Matrix{
public:
    template<typename = typename std::enable_if<Width == Height>::type>
    static Matrix
    Identity(){
        Matrix ret;
        for(int y = 0; y < Width; y++){
            // ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

EDIT 2020: (C++14)

template<int Width, int Height, typename T>
class Matrix{
public:
    template<typename = std::enable_if_t<Width == Height>>
    static Matrix
    Identity()
    {
        Matrix ret;
        for(int y = 0; y < Width; y++){
        //  ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

(C++20) https://godbolt.org/z/cs1MWj

template<int Width, int Height, typename T>
class Matrix{
public:
    static Matrix
    Identity()
        requires(Width == Height)
    {
        Matrix ret;
        for(int y = 0; y < Width; y++){
        //  ret.elements[y][y] = T(1);
        }
        return ret;
    }
};

Upvotes: 6

LostOfThought
LostOfThought

Reputation: 123

I found the answer to my question here: Using C++11 std::enable_if to enable...

In my solution, SFINAE occurs within my templated return type, therefore making the function template in itself valid. In the course of this, the function itself also becomes templated.

template<int Width, int Height, typename T>
class Matrix{
    template<typename EnabledType = T>
        static
        typename Matrix<Width, Height,
            typename std::enable_if<Width == Height, EnabledType>::type>
        Identity(){
        Matrix ret;
        for (int y = 0; y < Width; y++){
            ret.elements[y][y] = T(1);
        }
        return ret;
    }
}

Upvotes: 1

Related Questions