Search code examples
c++11templatestype-traitsstdarray

Why do std::arrays break templated functions with type checks?


I have a template function which performs certain actions depending on the type provided:

template<typename T>
T read()
{
    if (std::is_integral<T>()) {
        return static_cast<T>(std::stoi(readToken()));
    }
    else if (std::is_same<T, float>()) {
        return std::stof(readToken());
    }
    else if (std::is_same<T, std::array<float, 3>>()) {
        return { read<float>, read<float>, read<float> };
    }
    else throw std::logic_error("Invalid type");
}

The compiler gives the following warning when read<int>() is called:

control reaches end of non-void function


Solution

  • read is template. Every time this template is instantiated the whole body of the function is generated by compiler. When you call read<int>() section with array is also compiled, but it is not possible to assign array to return type which is int. That is why you get the error.

    Under g++7.3 you can use if constexpr construction. With this, lines within a scope the if condition is true are only compiled:

    template<typename T>
    T read() {
        if constexpr (std::is_integral<T>()) {
            return static_cast<T>(std::stoi(readToken()));
        }
        else if constexpr (std::is_same<T, float>()) {
            return std::stof(readToken());
        }
        else if constexpr (std::is_same<T, std::array<float, 3>>()) {
            std::array<float, 3> arr;
            arr.at(0) = 4; arr.at(1) = 2; arr.at(2) = 0;
            return arr;
        }
        else throw std::logic_error("Invalid type");
    }
    

    Prior to c++17, you call helper function which is overloaded for all types you want to process:

    template<class T>
    T readHelper() {
        throw std::runtime_error("invalid type");
    }
    
    // here provide types you want to be handled
    template<>
    int readHelper<int>() {  return std::stoi(readToken());  }
    
    template<>
    float readHelper<float>() { return std::stof(readToken()); }
    
    template<>
    std::array<float,3> readHelper<std::array<float,3>>() {
        return std::array<float,3>{};
    }
    
    template<class T>
    T read2(){
        return readHelper<T>();
    }
    

    Demo