Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.)

Thank you in advance for any help on this topic.

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.

like image 733
LostOfThought Avatar asked May 18 '13 01:05

LostOfThought


People also ask

Can we pass non-type parameters to templates?

Template non-type arguments in C++It is also possible to use non-type arguments (basic/derived data types) i.e., in addition to the type argument T, it can also use other arguments such as strings, function names, constant expressions, and built-in data types.

How will you restrict the template for a specific datatype?

There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What is the purpose of std :: Enable_if?

In C++ metaprogramming, std::enable_if is an important function to enable certain types for template specialization via some predicates known at the compile time. Using types that are not enabled by std::enable_if for template specialization will result in compile-time error.


2 Answers

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;
    }
};
like image 93
alfC Avatar answered Oct 21 '22 12:10

alfC


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;
    }
}
like image 27
LostOfThought Avatar answered Oct 21 '22 13:10

LostOfThought