Search code examples
c++c++11templatesenable-ifnon-type-template-parameter

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.


Solution

  • 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;
        }
    };