Search code examples
c++templatesc++14sfinaeenable-if

What are the syntax and semantics of C++ templated code?


template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}

I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.

Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?

Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)

EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if

EDIT2:

template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
    Fastor::Tensor<T,M,K> A; A.randInt();
}

This is literally the only thing i need changed. Notice the random() part

template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
    Fastor::Tensor<T,M,K> A; A.random();
}

Solution

  • I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.

    Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.

    Calling this function would look something like this:

    fastor2d<Object, 1, 2, 3>();
    

    In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.

    Here is a short SFINAE example:

    template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
    void function(T arg)
    {
    }
    
    function(0.3f);    //OK
    function(0.0);     //OK double results in std::is_floating_point<double>::value == true
    function("Hello"); //Does not exist (T is not floating point)
    

    The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.

    std::is_floating_point<std::string>::value == false
    

    Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.