0

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

metalfox
  • 6,301
  • 1
  • 21
  • 43
  • Please provide a [mre]. With emphasis on __minimal__. – Passer By Nov 23 '22 at 12:41
  • @PasserBy That's the most minimal example I could came up with by reduction from my codebase. Should I remove `f2` as it is only an example of other situations in which `template` isn't needed? – metalfox Nov 23 '22 at 12:44
  • 1
    You don't need `[[maybe_unused]]`, range adapters, vectors and arrays to demonstrate a syntax question, except in the rare case that you found a supremely intricate compiler bug. – Passer By Nov 23 '22 at 12:46
  • @PasserBy I couldn't reproduce without using `views::zip`. I added `[[maybe_unused]]` to silence unrelated warnings. – metalfox Nov 23 '22 at 12:52
  • [This](https://godbolt.org/z/EMTTh1rMo) is minimal. What you have is not. – Passer By Nov 23 '22 at 13:07
  • @PasserBy Sorry. I thought my example was sufficiently concise to show clearly what my problem was. Yours is clearly better as you were able to avoid the nested loops, which I couldn’t. – metalfox Nov 23 '22 at 14:16

1 Answers1

2

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.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • It was indeed a GCC bug, which has just been fixed by this patch: https://gcc.gnu.org/pipermail/gcc-patches/2022-November/607476.html Thanks! – metalfox Dec 02 '22 at 09:56