无法编译包含"的函数模板实例化,如果constexpr"

时间:2018-05-07 11:46:27

标签: c++ c++17 template-meta-programming constexpr

对于模糊的标题感到抱歉,但我无法想出更好的标题。

我写了一个扁平化容器的函数:

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);
    }
}

我希望它像:

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

这应该基本上将所有嵌套值从test复制到res,以便res == { 1, 2, 3, 4, 5, 6, 7 }

但是,如果我想编译代码,编译器会抛出一些错误,基本上说,else分支中的代码被编译而不是if constexpr块。

即。第一个实例化是void flatten<vector<vector<int>>, OutIt>(),这是正确的。第二个实例化(由else块触发)是void flatten<vector<int>, OutIt>(),这也是正确的。但对于第二次实例化,if constexpr表达式应评估为truevector<int>::value_typeintstd::iterator_traits<OutIt>::value_type>也为int。但不知何故,编译器试图实例化第三个模板void flatten<int, OutIt>(),导致编译器错误(因为我试图迭代一个整数)。

为什么编译器会实例化第3个模板?

1 个答案:

答案 0 :(得分:5)

您传递的std::back_insert_iterator value_typevoid。因此,if语句的真正分支永远不会被实例化。

一种可能的解决方案是尝试检查分支的功能。这可以使用std::is_assignable和一些涉及decltype(*declval<OutIt>())的样板来完成。