C++ - 如何调用递归继承的模板化基的模板化方法 class

C++ - How to call a templated method of a recursively inherited templated base class

我正在尝试创建一个 Tuple class,其中包含使用可变参数模板的任意类型的任意数量的条目,并且能够通过模板化获得第 n 个条目entry 方法,所以我可以按如下方式使用它:

Tuple<int, int, std::string, double> t(1, 2, "Hello World", 3.4);
std::cout << t.entry<1>() << std::endl; // Prints 2
std::cout << t.entry<2>() << std::endl; // Prints "Hello World"

我目前的做法:

template<typename ...Types>
struct Tuple;

template<typename Type>
struct Tuple<Type>
{
    Tuple(Type value) : value(value) { };
    Type value;

    template<int Index>
    Type& entry()
    {
        return value;
    }
};

template<typename Type, typename... Types>
struct Tuple<Type, Types...> : public Tuple<Types...>
{
    Tuple(Type value, Types ...args) : Tuple<Types...>(args...), value(value) { }
    Type value;

    template<int Index>
    auto entry() -> decltype(Tuple<Types...>::entry<Index-1>())&
    {
        return Tuple<Types...>::entry<Index-1>();
    }

    template<>
    Type& entry<0>() 
    {
        return value;
    }
};

第一个结构为一个元素提供“基本”情况,第二个结构递归地建立在它的基础上。但是,我收到错误

In member function ‘decltype (((Tuple<Types ...>::entry < (Index - 1)) > <expression error>))& Tuple<Type, Types ...>::entry()’:
error: expected primary-expression before ‘)’ token

如何调用模板化基础的模板化方法class?

你需要在::entry

前加一个template
template<int Index> // ...................VVVVVVVVV
auto entry() -> decltype(Tuple<Types...>::template entry<Index-1>())&
{ // .......................VVVVVVVVV
    return Tuple<Types...>::template entry<Index-1>();
}

::entry后的<被解析为关系运算符

但是你还有一个问题:entry()的特化:

template<>
Type& entry<0>() 
{
    return value;
}

不幸的是,如果不专门化包含 class.

的方法,则无法专门化方法

如果可以编译C++17,就可以避免方法特化而使用if constexpr

template <int Index>
auto & entry()
 {
   if constexpr ( Index == 0 )
      return value;
   else 
      return Tuple<Types...>::template entry<Index-1>();
 }

Pre C++17 在 C++14 中...我想你可以使用标签调度来解决

template <int>
Type & entry_helper (std::true_type)
 { return value; }

template <int Index>
auto & entry_helper (std::false_type)
 { return Tuple<Types...>::template entry<Index-1>(); }

template <int Index>
auto & entry()
{ return entry_helper<Index>(std::integral_constant<bool, Index==0>{}); }

在 C++11 中,对于 entry() 和第二个 entry_helper()[=28,您还需要输入尾随 return =]

正如 Patrick Roberts 所指出的(谢谢!)解决方案,添加尾随 return 类型,适用于带有 g++ 的 C++11 但不适用于 clang++,用于检测 return 的问题在递归上下文中键入。

对于 C++11,我提出了一个完全不同的解决方案,它避免了 entry()/entry_helper() 递归,但在 class 级别添加了另一个间接级别(添加递归基础 class 结构 Tpl)。还为 entry()entry_helper() 添加完美转发、无符号索引和常量版本。

#include <utility>
#include <iostream>
#include <type_traits>

template <std::size_t, typename...>
struct Tpl
 { void entry_helper () {} };

template <std::size_t I, typename T, typename ... Ts>
struct Tpl<I, T, Ts...> : public Tpl<I+1u, Ts...>
 {
   using Tpl<I+1, Ts...>::entry_helper;

   Tpl (T && t, Ts && ... ts)
      : Tpl<I+1u, Ts...>{std::forward<Ts>(ts)...}, value{std::forward<T>(t)}
    { }

   T value;

   T & entry_helper (std::integral_constant<std::size_t, I>)
    { return value; }

   T const & entry_helper (std::integral_constant<std::size_t, I>) const
    { return value; }
 };

template <typename ... Ts>
struct Tuple : public Tpl<0, Ts...>
 {
   using Tpl<0, Ts...>::entry_helper;

   Tuple (Ts && ... ts) : Tpl<0u, Ts...>{std::forward<Ts>(ts)...}
    { }

   template <std::size_t I>
   auto entry ()
    -> decltype(entry_helper(std::integral_constant<std::size_t, I>{})) &
    { return entry_helper(std::integral_constant<std::size_t, I>{}); }

   template <std::size_t I>
   auto entry () const
    -> decltype(entry_helper(std::integral_constant<std::size_t, I>{})) const &
    { return entry_helper(std::integral_constant<std::size_t, I>{}); }
 };

int main()
 {
   Tuple<int, int, std::string, double> t(1, 2, "Hello World", 3.4);

   std::cout << t.entry<1>() << std::endl; // Prints 2
   std::cout << t.entry<2>() << std::endl; // Prints "Hello World"
 }

假设您至少使用 C++14,您可以使用 auto& instead of a trailing return type, and as pointed out by 您需要在语法中的成员函数调用之前添加 template 关键字。

您还可以简化您的定义,因为您只需要一个空基 class,而不是为一种类型实现特化的 class;您的第二个 class 已经为一种类型实现了必要的行为。如果您使用的是 C++17

,则此简化要求您使用 std::enable_if, or if constexpr 重写 entry()
// only needed for C++14 when using std::enable_if
#include <type_traits>

template<typename...>
struct Tuple
{
};

template<typename T, typename... Ts>
struct Tuple<T, Ts...> : public Tuple<Ts...>
{
    Tuple(T value, Ts ...args) : value(value), Tuple<Ts...>(args...) { }
    T value;

    template<std::size_t I, std::enable_if_t<I == 0, bool> = true>
    T& entry() { return value; }

    template<std::size_t I, std::enable_if_t<I != 0, bool> = true>
    auto& entry() { return Tuple<Ts...>::template entry<I - 1>(); }

    // // requires C++17 support
    // template<std::size_t I>
    // auto& entry() {
    //     if constexpr (I == 0) { return value; }
    //     else { return Tuple<Ts...>::template entry<I - 1>(); }
    // }
};

godbolt.org

上试用