Home > Back-end >  Somewhat inconsistent need for template disambiguator
Somewhat inconsistent need for template disambiguator

Time:11-24

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.

  • Related