Reputation: 9835
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?
Upvotes: 5
Views: 103
Reputation: 170269
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>())
.
Upvotes: 5