In the example below, I need to use the template
disambiguator in the line marked as #1, while it appears to be unnecessary in the other occurrence of a similar pattern. What does it make the difference?
#include <cstdint>
#include <utility>
#include <array>
#include <vector>
#include <ranges>
template<std::size_t n, typename TFn>
constexpr void constexpr_for(TFn Fn)
{
[&Fn]<std::size_t... i>(std::index_sequence<i...>)
{ (Fn(std::integral_constant<std::size_t, i>{}), ...); } (std::make_index_sequence<n>{});
}
struct TVertex {};
class TFace
{
public:
static constexpr std::size_t NVertex = 3u;
private:
std::array<TVertex const *, NVertex> VertexVec;
public:
template<std::size_t i>
TVertex const &GetVertex() const
{ return *std::get<i>(VertexVec); }
};
void f(std::vector<TFace> const &FaceVec)
{
for (auto const &[i, Face1] : std::views::zip(std::views::iota(0u), FaceVec))
constexpr_for<TFace::NVertex>([&](auto const j)
{
for (auto const &[_, Face2] : std::views::zip(std::views::iota(0u), FaceVec)
| std::views::drop(i 1u))
constexpr_for<TFace::NVertex>([&](auto const k)
{
TVertex const &Vertex1 = Face1.GetVertex<j>();
TVertex const &Vertex2 = Face2.template GetVertex<k>(); // #1
});
});
}
I’m using GCC trunk. Here’s a Compiler Explorer link: https://godbolt.org/z/Kh6n6G4hW
CodePudding user response:
Here's a minimal way to reproduce your error:
#include <utility>
class TFace
{
public:
template<int i>
void GetVertex() const {}
};
template<typename>
void f1()
{
auto const &[_, Face2] = std::pair<int, TFace>{};
Face2.GetVertex<0>(); // #1
}
Since this compiles on clang, I'm thinking that this is a GCC bug. GCC seems to think that Face2
is a dependent name, even though it clearly isn't (The types of all the variables in:
std::views::zip(std::views::iota(0u), FaceVec)
| std::views::drop(i 1u)
are not dependent on any template argument).
This bug does have a simple workaround fortunately, adding Face2.template GetVertex
as you have. It might look better or be less confusing if you wrote Face1.template GetVertex
as well.
My hypothesis is that in the structured binding, Face2
is initialized similarly to get<1>(*zip_iterator)
with ADL, and GCC incorrectly assumes that this is dependent (even though zip_iterator
is not dependent).
The reason Face1
works without .template
is that it is defined in f
which is not a template, so it cannot possibly be dependent. Face2
is templated, since it is in a generic lambda, even if it doesn't end up being dependent.