0

I have a template function which recurses over a parameter pack. In essence it is meant to map something.Get<A,B,C>() into something.Get<A>().Get<B>().Get<C>().

This can be achieved by doing (full stand-alone source below the fold)

template <typename... Pack> class Struct {
  std::tuple<Pack...> mTuple;
  template <typename Type> auto &GetRef_internal() {
    return std::get<first_true<std::is_same<Type, Pack>::value...>::value>(
        mTuple);
  }
public:
  template <typename Current> Current &GetRef() {
    return GetRef_internal<Current>();
  }
  template <typename Current, typename... Next,
            typename std::enable_if<sizeof...(Next) != 0>::type * = nullptr>
  auto &GetRef() {
    auto current = GetRef_internal<Current>();
    return current.GetRef<Next...>();
  }
};

where first_true returns the index of the first element which is true.

This compiles with g++, and seemingly on MSVC too using an online compiler. When compiling with clang++ I get the following error though:

test.cxx:40:31: error: expected '(' for function-style cast or type construction
    return current.GetRef<Next...>();
                          ~~~~^
test.cxx:38:9: error: cannot deduce return type 'auto &' for function with no return statements
  auto &GetRef() {
        ^
test.cxx:48:12: note: in instantiation of function template specialization 'Struct<Struct<int, Struct<float, float> >, Struct<char>, int>::GetRef<Struct<int, Struct<float, float> >, Struct<float, float> , nullptr>' requested here
          .GetRef<Struct<int, Struct<float, float>>, Struct<float, float>>();
           ^
2 errors generated.

What could cause this?

p.s. The actual 'production code' is more useful than the example makes it seem, but that would be too much to post here.

=========================================================================

#include <tuple>
#include <type_traits>

// Template to check if condition holds true for all members of a parameter
// pack.
template <bool... b> struct BoolArray {};
template <bool... b>
using all_true = std::is_same<BoolArray<b...>, BoolArray<(b, true)...>>;

//helper type trait
template <bool... b> struct first_true {
  template <
      unsigned index = 0,
      typename std::enable_if<index<sizeof...(b)-1>::type * =
                                  nullptr> static constexpr unsigned check() {
    return std::get<index>(std::make_tuple(b...)) ? index : check<index + 1>();
  }
  template <unsigned index = 0,
            typename std::enable_if<index >= sizeof...(b)-1>::type * = nullptr>
  static constexpr unsigned check() {
    return std::get<index>(std::make_tuple(b...)) ? index : 0;
  }
  static constexpr unsigned value = first_true<b...>::check();
};

//The actual problem struct
template <typename... Pack> class Struct {
  std::tuple<Pack...> mTuple;
  template <typename Type> auto &GetRef_internal() {
    return std::get<first_true<std::is_same<Type, Pack>::value...>::value>(
        mTuple);
  }
public:
  template <typename Current> Current &GetRef() {
    return GetRef_internal<Current>();
  }
  template <typename Current, typename... Next,
            typename std::enable_if<sizeof...(Next) != 0>::type * = nullptr>
  auto &GetRef() {
    auto current = GetRef_internal<Current>();
    return current.GetRef<Next...>();
  }
};

int main() {
  // Define a random nested struct
  Struct<Struct<int, Struct<float, float>>, Struct<char>, int> myStruct;
  // Then retrieve one of the substructures to instantiate the template
  auto substruct =
      myStruct
          .GetRef<Struct<int, Struct<float, float>>, Struct<float, float>>();
 return 0;
}
Coding Cat
  • 13
  • 4

1 Answers1

0

current.GetRef<Next...> is a dependent name, so you need to specify that GetRef names a template using the template keyword:

return current.template GetRef<Next...>();

See Where and why do I have to put the "template" and "typename" keywords? for more information about dependent names.

Community
  • 1
  • 1
TartanLlama
  • 61,102
  • 13
  • 147
  • 188
  • This worked! learned something new today. Will accept this answer as soon as it lets me. Interesting that g++ resolves it even with -pedantic. – Coding Cat Feb 10 '17 at 14:13
  • @CodingCat the spec says if you have "foo.bar – Johannes Schaub - litb Feb 11 '17 at 16:41
  • The entire purpose of the "template" disambiguator is to help the compiler decide whether or not the name is a template. But GCC decides to do this on its own, without using the disambiguator, and therefore finds the "GetRef" in the context of the expression as a member. I don't think that this GCC behavior is correct. – Johannes Schaub - litb Feb 11 '17 at 16:43
  • I don't have a local installation of MSVC but I linked to an online compiler in my post which seems to use it, and it to accepts my test program hence why I originally assumed Clang was in the wrong. – Coding Cat Feb 12 '17 at 20:58