Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot compile function template instantiation containing "if constexpr"

Sorry for the vague title, but I couldn't come up with a better one.

I wrote a function to flatten containers:

template <typename Container, typename OutIt>
void flatten(const Container& container, OutIt res)
{
    if constexpr (std::is_convertible_v<typename Container::value_type, typename std::iterator_traits<OutIt>::value_type>)
    {
        for (const auto& value : container)
        {
            *res = value;
            ++res;
        }
    }
    else
    {
        for (const auto& subContainer : container)
            flatten(subContainer, res);
    }
}

And I wanted it to be used like:

vector<vector<int>> test = {{1}, {2, 3, 4}, {5, 6}, {7}};
vector<int> res;
flatten(test, std::back_inserter(res));

This should basically copy all the nested values from test to res, so that res == { 1, 2, 3, 4, 5, 6, 7 }.

However, if I want to compile the code, the compiler throws some errors which basically say, that the code in the else branch gets compiled instead of the if constexpr block.

I.e. The first instantiation is void flatten<vector<vector<int>>, OutIt>() which is correct. The second instantiation (which is triggered by the else block) is void flatten<vector<int>, OutIt>() which is also correct. But for the 2nd instantiation, the if constexpr expression should evaluate to true, as vector<int>::value_type is int and std::iterator_traits<OutIt>::value_type> is also int. But somehow the compiler tries to instantiate a third template void flatten<int, OutIt>() which causes the compiler errors (because I try to iterate over an integer).

Why does the compiler instantiate the 3rd template?

like image 614
Timo Avatar asked May 07 '18 11:05

Timo


1 Answers

You pass in a std::back_insert_iterator, for which value_type is void. So your true branch of the if statement is never instantiated.

A possible solution would be to try checking for what the branch does. This may be done with std::is_assignable and some boilerplate involving decltype(*declval<OutIt>()).

like image 161
StoryTeller - Unslander Monica Avatar answered Sep 30 '22 04:09

StoryTeller - Unslander Monica