2017-02-10 9 views
0

У меня есть функция шаблона, которая повторяется над пакетом параметров. По существу, он предназначен для отображения something.Get<A,B,C>() в something.Get<A>().Get<B>().Get<C>().Рекурсивная функция шаблона не компилируется с помощью Clang?

Это может быть достигнуто путем делать (полный автономный источник ниже сгиба)

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

, где first_true возвращает индекс первого элемента, который является истинным.

Это компилируется с g ++, и, похоже, на MSVC тоже используется online compiler. При компиляции с clang ++ я получаю следующую ошибку:

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. 

Что может быть причиной этого?

p.s. Фактический «производственный код» более полезен, чем пример, кажется, но это было бы слишком много для публикации здесь.

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

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

ответ

0

current.GetRef<Next...> является зависимым именем, поэтому вам нужно указать, что GetRef имена шаблона используя ключевое слово template:

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

См Where and why do I have to put the "template" and "typename" keywords? для получения дополнительной информации о зависимых именах.

+0

Это сработало! сегодня узнали что-то новое. Примите этот ответ, как только это позволит мне. Интересно, что g ++ разрешает его даже с -pedantic. –

+0

@CodingCat spec говорит, что если у вас есть «foo.bar <», чтобы узнать, является ли «бар» шаблоном, сначала его нужно искать в классе «foo». И если «bar» там не найден, его нужно искать в контексте всего выражения. Но чтобы узнать, присутствует ли он в классе объекта, он должен сначала подождать, пока не будет создан экземпляр окружающего шаблона. По-видимому, Кланг делает это. GCC не делает, поэтому считает, что это шаблон. Это также проблема в https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576, я думаю. В этом сообщении GCC отвергает действительную программу. –

+0

Вся цель шаблона «шаблон» заключается в том, чтобы помочь компилятору решить, является ли это имя шаблоном. Но GCC решает сделать это самостоятельно, не используя disambiguator, и поэтому находит «GetRef» в контексте выражения в качестве члена. Я не думаю, что это поведение GCC верное. –